mirror of
https://github.com/Pridecraft-Studios/joy.git
synced 2025-08-03 06:26:00 -04:00
feat: Unnecessarily accurate voxel shapes for blahaj & friends
This commit is contained in:
parent
85507ce494
commit
7f137234f5
@ -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)));
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
138
xplat/src/main/java/gay/pridecraft/joy/util/VoxelEmitter.java
Normal file
138
xplat/src/main/java/gay/pridecraft/joy/util/VoxelEmitter.java
Normal 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();
|
||||
};
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user