feat: Unnecessarily accurate voxel shapes for blahaj & friends

This commit is contained in:
Ampflower 🌺 2025-03-14 03:43:56 -07:00
parent 85507ce494
commit 7f137234f5
No known key found for this signature in database
GPG Key ID: FC0397C90D508D7F
5 changed files with 449 additions and 14 deletions

View File

@ -8,18 +8,21 @@ import net.minecraft.block.Blocks;
import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public final class BlahajBlocks {
public static final Block
GRAY_SHARK_BLOCK = mkBlock("gray_shark", Blocks.LIGHT_GRAY_WOOL),
BLAHAJ_BLOCK = mkBlock("blue_shark", Blocks.CYAN_WOOL),
BLAVINGAD_BLOCK = mkBlock("blue_whale", Blocks.BLUE_WOOL),
BREAD_BLOCK = mkBlock("bread", Blocks.ORANGE_WOOL),
BROWN_BEAR_BLOCK = mkBlock("brown_bear", Blocks.BROWN_WOOL);
BLAVINGAD_BLOCK = mkBlock("blue_whale", Blocks.BLUE_WOOL, CuddlyBlock.WHALE_SHAPES),
BREAD_BLOCK = mkBlock("bread", Blocks.ORANGE_WOOL, CuddlyBlock.BREAD_SHAPES),
BROWN_BEAR_BLOCK = mkBlock("brown_bear", Blocks.BROWN_WOOL, CuddlyBlock.BEAR_SHAPES);
public static final Item
GRAY_SHARK_ITEM = mkItem(GRAY_SHARK_BLOCK),
@ -62,6 +65,10 @@ public final class BlahajBlocks {
} */
}
private static Block mkBlock(String id, Block block, Map<Direction, VoxelShape> shapes) {
return register(id, new CuddlyBlock(AbstractBlock.Settings.copy(block), shapes));
}
private static Block mkBlock(String id, Block block) {
return register(id, new CuddlyBlock(AbstractBlock.Settings.copy(block)));
}

View File

@ -1,6 +1,7 @@
package gay.pridecraft.joy.block;
import com.mojang.serialization.MapCodec;
import gay.pridecraft.joy.util.VoxelEmitter;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.HorizontalFacingBlock;
@ -12,18 +13,128 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
public class CuddlyBlock extends HorizontalFacingBlock {
protected static final VoxelShape SHAPE = Block.createCuboidShape(
4.0, 0.0, 4.0,
12.0, 8.0, 12.0
);
import java.util.Map;
public static final MapCodec<CuddlyBlock> CODEC = createCodec(CuddlyBlock::new);
public class CuddlyBlock extends HorizontalFacingBlock {
public static final Map<Direction, VoxelShape>
SHARK_SHAPES = VoxelEmitter.union(
// BASE
VoxelEmitter.ofBlock(
8 - 2, 0, 0,
8 + 2, 4, 9
),
// MID
VoxelEmitter.ofBlock(
8 - 1.5, 0.5, 9,
8 + 1.5, 3.5, 11
),
// FIN_RIGHT
VoxelEmitter.ofBlock(
8 - 3, -1.5, 5.5,
8 - 2, 1, 9.5
),
// FIN_LEFT
VoxelEmitter.ofBlock(
8 + 2, -1.5, 5.5,
8 + 3, 1, 9.5
),
// FIN_BACK
VoxelEmitter.ofBlock(
8 - 1, 4, 5.5,
8 + 1, 7, 8.5
),
// TAIL
VoxelEmitter.ofBlock(
8 - 1, 1, 11,
8 + 1, 3, 16
),
// TAIL_DETAIL
VoxelEmitter.ofBlock(
8 - .5, 3, 12,
8 + .5, 3.5, 13
),
// TAIL_FIN
VoxelEmitter.ofBlock(
8 - .5, -.5, 14.5,
8 + .5, 5.5, 17
)
).toFloorMap(FACING),
WHALE_SHAPES = VoxelEmitter.union(
// BASE
VoxelEmitter.ofBlock(
8 - 3, 0, 0,
8 + 3, 4, 8
),
// MID
VoxelEmitter.ofBlock(
8 - 2, 0.5, 8,
8 + 2, 3.5, 11
),
// FIN_RIGHT
VoxelEmitter.ofBlock(
8 - 6.5, 1, 3.5,
8 - 3, 2, 6
),
// FIN_LEFT
VoxelEmitter.ofBlock(
8 + 3, 1, 3.5,
8 + 6.5, 2, 6
),
// FIN_BACK
VoxelEmitter.ofBlock(
8 - .5, 4, 5.5,
8 + .5, 6.5, 8
),
// TAIL
VoxelEmitter.ofBlock(
8 - 1.5, 1, 11,
8 + 1.5, 3, 14
),
// TAIL_FIN
VoxelEmitter.ofBlock(
8 - 4, 1, 14,
8 + 4, 3, 16
)
).toFloorMap(FACING),
BREAD_SHAPES = VoxelEmitter.ofBlock(
8 - 3, 0, -1,
8 + 3, 6, 16
).toFloorMap(FACING),
BEAR_SHAPES = VoxelEmitter.union(
// BODY
VoxelEmitter.ofBlock(
8 - 6, 0, 4,
8 + 6, 10, 15
),
// HEAD
VoxelEmitter.ofBlock(
8 - 4, 10, 7,
8 + 4, 16, 14
),
// SNOUT
VoxelEmitter.ofBlock(
8 - 2, 10.5, 5,
8 + 2, 13.5, 7
)
).toFloorMap(FACING);
private static final MapCodec<CuddlyBlock> CODEC = createCodec(CuddlyBlock::new);
private final Map<Direction, VoxelShape> shapes;
public CuddlyBlock(Settings settings, Map<Direction, VoxelShape> shapes) {
super(settings);
this.shapes = shapes;
this.setDefaultState(this.getStateManager().getDefaultState()
.with(FACING, Direction.NORTH));
}
public CuddlyBlock(Settings settings) {
super(settings);
this.setDefaultState(this.getStateManager().getDefaultState()
.with(FACING, Direction.NORTH));
this(settings, SHARK_SHAPES);
}
@Override
@ -32,13 +143,13 @@ public class CuddlyBlock extends HorizontalFacingBlock {
}
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return SHAPE;
return shapes.get(state.get(FACING));
}
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return this.getDefaultState()
.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
.with(FACING, ctx.getHorizontalPlayerFacing().getOpposite());
}
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {

View File

@ -0,0 +1,79 @@
package gay.pridecraft.joy.util;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
/**
* VoxelShape emitter that rotates as needed.
*
* @author Ampflower
* @since 1.0.0
**/
record BasicVoxelEmitter(
double ax, double ay, double az,
double bx, double by, double bz
) implements VoxelEmitter {
BasicVoxelEmitter {
if (ax > bx) {
throw new IllegalArgumentException("x min > x max");
}
if (ay > by) {
throw new IllegalArgumentException("y min > y max");
}
if (az > bz) {
throw new IllegalArgumentException("z min > z max");
}
}
/**
* Identity of the emitter
*/
public VoxelShape floorNorth() {
return VoxelShapes.cuboid(ax, ay, az, bx, by, bz);
}
public VoxelShape floorSouth() {
return VoxelShapes.cuboid(1 - bx, ay, 1 - bz, 1 - ax, by, 1 - az);
}
public VoxelShape floorEast() {
return VoxelShapes.cuboid(1 - bz, ay, ax, 1 - az, by, bx);
}
public VoxelShape floorWest() {
return VoxelShapes.cuboid(az, ay, 1 - bx, bz, by, 1 - ax);
}
public VoxelShape ceilingNorth() {
return VoxelShapes.cuboid(ax, 1 - by, az, bx, 1 - ay, bz);
}
public VoxelShape ceilingSouth() {
return VoxelShapes.cuboid(1 - bx, 1 - by, 1 - bz, 1 - ax, 1 - ay, 1 - az);
}
public VoxelShape ceilingEast() {
return VoxelShapes.cuboid(1 - bz, 1 - by, ax, 1 - az, 1 - ay, bx);
}
public VoxelShape ceilingWest() {
return VoxelShapes.cuboid(az, 1 - by, 1 - bx, bz, 1 - ay, 1 - ax);
}
public VoxelShape north() {
return VoxelShapes.cuboid(ax, az, 1 - by, bx, bz, 1 - ay);
}
public VoxelShape south() {
return VoxelShapes.cuboid(1 - bx, az, ay, 1 - ax, bz, by);
}
public VoxelShape east() {
return VoxelShapes.cuboid(ay, az, ax, by, bz, bx);
}
public VoxelShape west() {
return VoxelShapes.cuboid(1 - by, az, 1 - bx, 1 - ay, bz, 1 - ax);
}
}

View File

@ -0,0 +1,100 @@
package gay.pridecraft.joy.util;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* @author Ampflower
* @since 1.0.0
**/
record UnionVoxelEmitter(List<VoxelEmitter> emitters) implements VoxelEmitter {
UnionVoxelEmitter(VoxelEmitter... emitters) {
this(toBasic(Arrays.asList(emitters)));
}
UnionVoxelEmitter(Iterable<VoxelEmitter> emitters) {
this(toBasic(emitters));
}
static List<VoxelEmitter> toBasic(Iterable<VoxelEmitter> emitters) {
final var set = new HashSet<VoxelEmitter>();
for (final var emitter : emitters) {
if (emitter instanceof UnionVoxelEmitter union) {
set.addAll(union.emitters());
} else {
set.add(emitter);
}
}
return List.copyOf(set);
}
static VoxelShape union(Stream<VoxelShape> shapes) {
final var list = shapes.toList();
if (list.isEmpty()) {
return VoxelShapes.empty();
}
final var init = list.get(0);
if (list.size() == 1) {
return init;
}
return VoxelShapes.union(init, list.subList(1, list.size()).toArray(VoxelShape[]::new));
}
private Stream<VoxelShape> stream(Function<VoxelEmitter, VoxelShape> map) {
return emitters.stream().map(map);
}
public VoxelShape floorNorth() {
return union(stream(VoxelEmitter::floorNorth));
}
public VoxelShape floorSouth() {
return union(stream(VoxelEmitter::floorSouth));
}
public VoxelShape floorEast() {
return union(stream(VoxelEmitter::floorEast));
}
public VoxelShape floorWest() {
return union(stream(VoxelEmitter::floorWest));
}
public VoxelShape ceilingNorth() {
return union(stream(VoxelEmitter::ceilingNorth));
}
public VoxelShape ceilingSouth() {
return union(stream(VoxelEmitter::ceilingSouth));
}
public VoxelShape ceilingEast() {
return union(stream(VoxelEmitter::ceilingEast));
}
public VoxelShape ceilingWest() {
return union(stream(VoxelEmitter::ceilingWest));
}
public VoxelShape north() {
return union(stream(VoxelEmitter::north));
}
public VoxelShape south() {
return union(stream(VoxelEmitter::south));
}
public VoxelShape east() {
return union(stream(VoxelEmitter::east));
}
public VoxelShape west() {
return union(stream(VoxelEmitter::west));
}
}

View File

@ -0,0 +1,138 @@
package gay.pridecraft.joy.util;
import net.minecraft.state.property.DirectionProperty;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import java.util.EnumMap;
/**
* @author Ampflower
* @since 1.0.0
**/
public sealed interface VoxelEmitter permits BasicVoxelEmitter, UnionVoxelEmitter {
double BLOCK = 16;
/**
* Expected: Floor north box in 1x1x1 bound
*/
static VoxelEmitter of(final double ax, final double ay, final double az,
final double bx, final double by, final double bz) {
return new BasicVoxelEmitter(ax, ay, az, bx, by, bz);
}
/**
* Expected: Floor north box in 16x16x16 bound
*/
static VoxelEmitter ofBlock(final double ax, final double ay, final double az,
final double bx, final double by, final double bz) {
return new BasicVoxelEmitter(
ax / BLOCK, ay / BLOCK, az / BLOCK,
bx / BLOCK, by / BLOCK, bz / BLOCK
);
}
static VoxelEmitter union(VoxelEmitter... emitters) {
if (emitters.length == 1) {
return emitters[0];
}
return new UnionVoxelEmitter(emitters);
}
static VoxelEmitter union(Iterable<VoxelEmitter> emitters) {
return new UnionVoxelEmitter(emitters);
}
VoxelShape floorNorth();
VoxelShape floorSouth();
VoxelShape floorEast();
VoxelShape floorWest();
VoxelShape ceilingNorth();
VoxelShape ceilingSouth();
VoxelShape ceilingEast();
VoxelShape ceilingWest();
VoxelShape north();
VoxelShape south();
VoxelShape east();
VoxelShape west();
default EnumMap<Direction, VoxelShape> toWallMap(Direction... directions) {
final var ret = new EnumMap<Direction, VoxelShape>(Direction.class);
for (final Direction direction : directions) {
ret.put(direction, ofWall(direction));
}
return ret;
}
default EnumMap<Direction, VoxelShape> toFloorMap(Direction... directions) {
final var ret = new EnumMap<Direction, VoxelShape>(Direction.class);
for (final Direction direction : directions) {
ret.put(direction, ofFloor(direction));
}
return ret;
}
default EnumMap<Direction, VoxelShape> toCeilingMap(Direction... directions) {
final var ret = new EnumMap<Direction, VoxelShape>(Direction.class);
for (final Direction direction : directions) {
ret.put(direction, ofCeiling(direction));
}
return ret;
}
default EnumMap<Direction, VoxelShape> toWallMap(DirectionProperty property) {
return toWallMap(property.getValues().toArray(Direction[]::new));
}
default EnumMap<Direction, VoxelShape> toFloorMap(DirectionProperty property) {
return toFloorMap(property.getValues().toArray(Direction[]::new));
}
default EnumMap<Direction, VoxelShape> toCeilingMap(DirectionProperty property) {
return toCeilingMap(property.getValues().toArray(Direction[]::new));
}
default VoxelShape ofWall(Direction direction) {
return switch (direction) {
case DOWN -> floorNorth();
case UP -> ceilingNorth();
case NORTH -> north();
case SOUTH -> south();
case WEST -> west();
case EAST -> east();
};
}
default VoxelShape ofFloor(Direction direction) {
return switch (direction) {
case UP -> ceilingNorth();
case DOWN, NORTH -> floorNorth();
case SOUTH -> floorSouth();
case WEST -> floorWest();
case EAST -> floorEast();
};
}
default VoxelShape ofCeiling(Direction direction) {
return switch (direction) {
case DOWN -> floorNorth();
case UP, NORTH -> ceilingNorth();
case SOUTH -> ceilingSouth();
case WEST -> ceilingWest();
case EAST -> ceilingEast();
};
}
}