started new stairs reimplementation shapes are not all there for generation and inner shapes are badly oriented

This commit is contained in:
Adrien1106 2024-02-13 16:55:42 +01:00
parent a788176ffc
commit 7b4c465c66
10 changed files with 643 additions and 86 deletions

View File

@ -31,16 +31,12 @@ import java.util.stream.Collectors;
public class Templates implements ModInitializer {
public static final String MODID = "reframedtemplates";
//addon devs: *Don't* add your blocks to this collection, it's just for my registration convenience since Templates adds a lot of blocks...
@ApiStatus.Internal static final ArrayList<Block> INTERNAL_TEMPLATES = new ArrayList<>();
@ApiStatus.Internal static Block CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE;
protected static final ArrayList<Block> INTERNAL_TEMPLATES = new ArrayList<>();
public static Block CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE;
//For addon devs: Please don't stuff more blocks into this BlockEntityType, and register your own.
//You can even re-register the same TemplateEntity class under your own ID if you like. (It's an extensible block entity.)
@ApiStatus.Internal public static BlockEntityType<TemplateEntity> TEMPLATE_BLOCK_ENTITY;
public static BlockEntityType<FramedEntity> TEMPLATE_BLOCK_ENTITY;
//Changed in TemplatesClient (which is safe since client initializers load after common initializers)
@ApiStatus.Internal public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
@Override
public void onInitialize() {
@ -69,7 +65,7 @@ public class Templates implements ModInitializer {
//The block entity is still called templates:slope; this is a bit of a legacy mistake.
TEMPLATE_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("slope"),
FabricBlockEntityTypeBuilder.create((pos, state) -> new TemplateEntity(TEMPLATE_BLOCK_ENTITY, pos, state), INTERNAL_TEMPLATES.toArray(new Block[0])).build(null)
FabricBlockEntityTypeBuilder.create((pos, state) -> new FramedEntity(TEMPLATE_BLOCK_ENTITY, pos, state), INTERNAL_TEMPLATES.toArray(new Block[0])).build(null)
);
Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder()
@ -79,7 +75,6 @@ public class Templates implements ModInitializer {
);
}
//purely to shorten this call :p
private static AbstractBlock.Settings cp(Block base) {
return TemplateInteractionUtil.configureSettings(AbstractBlock.Settings.copy(base));
}

View File

@ -58,8 +58,9 @@ public class TemplatesClient implements ClientModInitializer {
api.addTemplateModel(Templates.id("glass_pane_noside_alt_special"), api.auto(new Identifier("block/glass_pane_noside_alt")));
api.addTemplateModel(Templates.id("pressure_plate_up_special") , api.auto(new Identifier("block/pressure_plate_up")));
api.addTemplateModel(Templates.id("pressure_plate_down_special") , api.auto(new Identifier("block/pressure_plate_down")));
api.addTemplateModel(Templates.id("slab_special") , api.auto(new Identifier("block/slab")));
api.addTemplateModel(Templates.id("slab_special") , api.auto(new Identifier("block/slab")));
api.addTemplateModel(Templates.id("stairs_special") , api.auto(new Identifier("block/stairs")));
api.addTemplateModel(Templates.id("double_outer_stairs_special") , api.auto(Templates.id("block/double_outer_stairs")));
api.addTemplateModel(Templates.id("inner_stairs_special") , api.auto(new Identifier("block/inner_stairs")));
api.addTemplateModel(Templates.id("outer_stairs_special") , api.auto(new Identifier("block/outer_stairs")));
api.addTemplateModel(Templates.id("trapdoor_bottom_special") , api.auto(new Identifier("block/template_trapdoor_bottom")));
@ -92,7 +93,7 @@ public class TemplatesClient implements ClientModInitializer {
api.assignItemModel(Templates.id("trapdoor_bottom_special") , Templates.IRON_TRAPDOOR);
api.assignItemModel(Templates.id("fence_post_inventory_special") , Templates.POST);
api.assignItemModel(Templates.id("pressure_plate_up_special") , Templates.PRESSURE_PLATE);
api.assignItemModel(Templates.id("slab_bottom_special") , Templates.SLAB);
api.assignItemModel(Templates.id("slab_special") , Templates.SLAB);
api.assignItemModel(Templates.id("stairs_special") , Templates.STAIRS);
api.assignItemModel(Templates.id("trapdoor_bottom_special") , Templates.TRAPDOOR);
api.assignItemModel(Templates.id("wall_inventory_special") , Templates.WALL);

View File

@ -1,6 +1,6 @@
package fr.adrien1106.reframedtemplates.api;
import fr.adrien1106.reframedtemplates.block.TemplateEntity;
import fr.adrien1106.reframedtemplates.block.FramedEntity;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
@ -61,11 +61,11 @@ public class TemplateInteractionUtil {
}
public static @Nullable BlockState modifyPlacementState(@Nullable BlockState in, ItemPlacementContext ctx) {
return TemplateEntity.weirdNbtLightLevelStuff(in, ctx.getStack());
return FramedEntity.weirdNbtLightLevelStuff(in, ctx.getStack());
}
public static ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if(!(world.getBlockEntity(pos) instanceof TemplateEntity be)) return ActionResult.PASS;
if(!(world.getBlockEntity(pos) instanceof FramedEntity be)) return ActionResult.PASS;
if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS;
ItemStack held = player.getStackInHand(hand);
@ -132,7 +132,7 @@ public class TemplateInteractionUtil {
//Maybe an odd spot to put this logic but it's consistent w/ vanilla chests, barrels, etc
public static void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
if(!state.isOf(newState.getBlock()) &&
world.getBlockEntity(pos) instanceof TemplateEntity template &&
world.getBlockEntity(pos) instanceof FramedEntity template &&
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
) {
DefaultedList<ItemStack> drops = DefaultedList.of();
@ -152,7 +152,7 @@ public class TemplateInteractionUtil {
public static void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
//Load the BlockEntityTag clientside, which fixes the template briefly showing its default state when placing it.
//I'm surprised this doesn't happen by default; the BlockEntityTag stuff is only done serverside.
if(world.isClient && world.getBlockEntity(pos) instanceof TemplateEntity be) {
if(world.isClient && world.getBlockEntity(pos) instanceof FramedEntity be) {
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
if(tag != null) be.readNbt(tag);
}
@ -160,7 +160,7 @@ public class TemplateInteractionUtil {
//Returns "null" to signal "no opinion". Imagine it like an InteractionResult.PASS.
public static @Nullable VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return view.getBlockEntity(pos) instanceof TemplateEntity be && !be.isSolid() ? VoxelShapes.empty() : null;
return view.getBlockEntity(pos) instanceof FramedEntity be && !be.isSolid() ? VoxelShapes.empty() : null;
}
public static boolean emitsRedstonePower(BlockState state) {
@ -169,11 +169,11 @@ public class TemplateInteractionUtil {
}
public static int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return view.getBlockEntity(pos) instanceof TemplateEntity be && be.emitsRedstone() ? 15 : 0;
return view.getBlockEntity(pos) instanceof FramedEntity be && be.emitsRedstone() ? 15 : 0;
}
public static int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return view.getBlockEntity(pos) instanceof TemplateEntity be && be.emitsRedstone() ? 15 : 0;
return view.getBlockEntity(pos) instanceof FramedEntity be && be.emitsRedstone() ? 15 : 0;
}
public static int luminance(BlockState state) {

View File

@ -26,7 +26,7 @@ import java.util.Objects;
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic,
//is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon.
//To that end, most of the state has been crammed into a bitfield.
public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity {
public class FramedEntity extends BlockEntity implements ThemeableBlockEntity {
protected BlockState renderedState = Blocks.AIR.getDefaultState();
protected byte bitfield = DEFAULT_BITFIELD;
@ -43,7 +43,7 @@ public class TemplateEntity extends BlockEntity implements ThemeableBlockEntity
protected static final String BLOCKSTATE_KEY = "s";
protected static final String BITFIELD_KEY = "b";
public TemplateEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
public FramedEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}

View File

@ -1,96 +1,393 @@
package fr.adrien1106.reframedtemplates.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframedtemplates.Templates;
import fr.adrien1106.reframedtemplates.api.TemplateInteractionUtil;
import fr.adrien1106.reframedtemplates.generator.GBlockstate;
import fr.adrien1106.reframedtemplates.generator.MultipartBlockStateProvider;
import fr.adrien1106.reframedtemplates.util.VoxelHelper;
import fr.adrien1106.reframedtemplates.util.property.StairDirection;
import fr.adrien1106.reframedtemplates.util.property.StairShape;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.StairsBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.data.client.*;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.state.property.EnumProperty;
import net.minecraft.util.Identifier;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
public class TemplateStairsBlock extends StairsBlock implements BlockEntityProvider {
public TemplateStairsBlock(BlockState blockState, Settings settings) {
super(blockState, settings);
setDefaultState(TemplateInteractionUtil.setDefaultStates(getDefaultState()));
}
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static fr.adrien1106.reframedtemplates.util.property.StairShape.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static fr.adrien1106.reframedtemplates.util.property.StairDirection.*;
public class TemplateStairsBlock extends WaterloggableTemplateBlock implements MultipartBlockStateProvider {
public static final EnumProperty<StairDirection> FACING = EnumProperty.of("facing", StairDirection.class);
public static final EnumProperty<StairShape> SHAPE = EnumProperty.of("shape", StairShape.class);
private static final List<VoxelShape> VOXEL_LIST= new ArrayList<>();
public TemplateStairsBlock(Settings settings) {
this(Blocks.OAK_PLANKS.getDefaultState(), settings);
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return Templates.TEMPLATE_BLOCK_ENTITY.instantiate(pos, state);
super(settings);
setDefaultState(getDefaultState().with(FACING, StairDirection.NORTH_DOWN).with(SHAPE, STRAIGHT));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(TemplateInteractionUtil.appendProperties(builder));
super.appendProperties(builder.add(FACING).add(SHAPE));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
.with(SHAPE, getPlacementShape(state.get(FACING), world, pos));
}
@Nullable
@Override
@Override // Pretty happy of how clean it is (also got it on first try :) )
public BlockState getPlacementState(ItemPlacementContext ctx) {
return TemplateInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
BlockState state = super.getPlacementState(ctx);
Direction side = ctx.getSide().getOpposite();
BlockPos block_pos = ctx.getBlockPos();
Vec3d hit_pos = ctx.getHitPos();
Vec3d pos = new Vec3d(
hit_pos.getX() - block_pos.getX() - .5d,
hit_pos.getY() - block_pos.getY() - .5d,
hit_pos.getZ() - block_pos.getZ() - .5d
);
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = TemplateInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
Stream<Axis> axes = Stream.of(Axis.values()).filter(axis -> !axis.equals(side.getAxis()));
Axis axis = axes.reduce((axis_1, axis_2) ->
Math.abs(axis_1.choose(pos.x, pos.y, pos.z)) > Math.abs(axis_2.choose(pos.x, pos.y, pos.z))
? axis_1
: axis_2
).get();
Direction part_direction = Direction.from(
axis,
axis.choose(pos.x, pos.y, pos.z) > 0
? Direction.AxisDirection.POSITIVE
: Direction.AxisDirection.NEGATIVE
);
return state.with(FACING, StairDirection.getByDirections(side, part_direction));
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
TemplateInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
//StairsBlock onStateReplaced is Weird! it doesn't delegate to regular block onStateReplaced!
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
}
/* ---------------------------------- DON'T GO FURTHER IF YOU LIKE HAVING EYES ---------------------------------- */
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
TemplateInteractionUtil.onPlaced(world, pos, state, placer, stack);
super.onPlaced(world, pos, state, placer, stack);
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
StairShape shape = state.get(SHAPE);
StairDirection direction = state.get(FACING);
return switch (shape) {
case STRAIGHT ->
switch (direction) {
case DOWN_SOUTH -> VOXEL_LIST.get(0);
case NORTH_DOWN -> VOXEL_LIST.get(1);
case UP_NORTH -> VOXEL_LIST.get(2);
case SOUTH_UP -> VOXEL_LIST.get(3);
case DOWN_EAST -> VOXEL_LIST.get(4);
case WEST_DOWN -> VOXEL_LIST.get(5);
case UP_WEST -> VOXEL_LIST.get(6);
case EAST_UP -> VOXEL_LIST.get(7);
case NORTH_EAST -> VOXEL_LIST.get(8);
case EAST_SOUTH -> VOXEL_LIST.get(9);
case SOUTH_WEST -> VOXEL_LIST.get(10);
case WEST_NORTH -> VOXEL_LIST.get(11);
};
case INNER_LEFT ->
switch (direction) {
case WEST_DOWN, NORTH_DOWN -> VOXEL_LIST.get(44);
case DOWN_EAST -> VOXEL_LIST.get(45);
case DOWN_SOUTH -> VOXEL_LIST.get(47);
case UP_WEST, UP_NORTH, WEST_NORTH -> VOXEL_LIST.get(48);
case EAST_UP, NORTH_EAST -> VOXEL_LIST.get(49);
case EAST_SOUTH -> VOXEL_LIST.get(50);
case SOUTH_UP, SOUTH_WEST -> VOXEL_LIST.get(51);
};
case INNER_RIGHT ->
switch (direction) {
case WEST_NORTH -> VOXEL_LIST.get(44);
case NORTH_DOWN, NORTH_EAST -> VOXEL_LIST.get(45);
case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> VOXEL_LIST.get(46);
case WEST_DOWN, SOUTH_WEST -> VOXEL_LIST.get(47);
case UP_NORTH -> VOXEL_LIST.get(49);
case EAST_UP, SOUTH_UP -> VOXEL_LIST.get(50);
case UP_WEST -> VOXEL_LIST.get(51);
};
case OUTER_LEFT ->
switch (direction) {
case DOWN_EAST -> VOXEL_LIST.get(43);
case WEST_DOWN, NORTH_DOWN -> VOXEL_LIST.get(42);
case DOWN_SOUTH -> VOXEL_LIST.get(41);
case EAST_UP, NORTH_EAST -> VOXEL_LIST.get(39);
case UP_WEST, UP_NORTH, WEST_NORTH -> VOXEL_LIST.get(38);
case SOUTH_UP, SOUTH_WEST -> VOXEL_LIST.get(37);
case EAST_SOUTH -> VOXEL_LIST.get(36);
};
case OUTER_RIGHT ->
switch (direction) {
case NORTH_DOWN, NORTH_EAST -> VOXEL_LIST.get(43);
case WEST_NORTH -> VOXEL_LIST.get(42);
case WEST_DOWN, SOUTH_WEST -> VOXEL_LIST.get(41);
case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> VOXEL_LIST.get(40);
case UP_NORTH -> VOXEL_LIST.get(39);
case UP_WEST -> VOXEL_LIST.get(37);
case EAST_UP, SOUTH_UP -> VOXEL_LIST.get(36);
};
case FIRST_OUTER_LEFT ->
switch (direction) {
case WEST_DOWN, NORTH_DOWN -> VOXEL_LIST.get(14);
case SOUTH_UP -> VOXEL_LIST.get(17);
case EAST_UP -> VOXEL_LIST.get(19);
case EAST_SOUTH -> VOXEL_LIST.get(20);
case DOWN_SOUTH -> VOXEL_LIST.get(22);
case UP_NORTH, WEST_NORTH -> VOXEL_LIST.get(25);
case SOUTH_WEST -> VOXEL_LIST.get(28);
case UP_WEST -> VOXEL_LIST.get(31);
case DOWN_EAST -> VOXEL_LIST.get(34);
case NORTH_EAST -> VOXEL_LIST.get(35);
};
case FIRST_OUTER_RIGHT ->
switch (direction) {
case NORTH_DOWN -> VOXEL_LIST.get(15);
case SOUTH_UP, EAST_UP -> VOXEL_LIST.get(16);
case WEST_DOWN -> VOXEL_LIST.get(13);
case DOWN_SOUTH, EAST_SOUTH -> VOXEL_LIST.get(23);
case UP_NORTH -> VOXEL_LIST.get(24);
case WEST_NORTH -> VOXEL_LIST.get(26);
case UP_WEST -> VOXEL_LIST.get(28);
case SOUTH_WEST -> VOXEL_LIST.get(29);
case DOWN_EAST -> VOXEL_LIST.get(33);
case NORTH_EAST -> VOXEL_LIST.get(34);
};
case SECOND_OUTER_LEFT ->
switch (direction) {
case DOWN_EAST -> VOXEL_LIST.get(15);
case DOWN_SOUTH -> VOXEL_LIST.get(13);
case UP_WEST, UP_NORTH -> VOXEL_LIST.get(18);
case SOUTH_UP, SOUTH_WEST -> VOXEL_LIST.get(21);
case NORTH_EAST -> VOXEL_LIST.get(24);
case NORTH_DOWN -> VOXEL_LIST.get(26);
case WEST_DOWN -> VOXEL_LIST.get(30);
case WEST_NORTH -> VOXEL_LIST.get(31);
case EAST_SOUTH -> VOXEL_LIST.get(32);
case EAST_UP -> VOXEL_LIST.get(35);
};
case SECOND_OUTER_RIGHT ->
switch (direction) {
case DOWN_SOUTH, DOWN_EAST -> VOXEL_LIST.get(12);
case UP_WEST -> VOXEL_LIST.get(17);
case UP_NORTH -> VOXEL_LIST.get(19);
case SOUTH_UP -> VOXEL_LIST.get(20);
case SOUTH_WEST -> VOXEL_LIST.get(22);
case NORTH_EAST, NORTH_DOWN -> VOXEL_LIST.get(27);
case WEST_DOWN -> VOXEL_LIST.get(29);
case WEST_NORTH -> VOXEL_LIST.get(30);
case EAST_UP -> VOXEL_LIST.get(32);
case EAST_SOUTH -> VOXEL_LIST.get(33);
};
};
}
private static String getNeighborPos(StairDirection face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos) {
BlockState block_state = world.getBlockState(
pos.offset(reverse ? direction.getOpposite() : direction)
);
if (block_state.getBlock() instanceof TemplateStairsBlock && block_state.get(FACING).hasDirection(reference)) {
if (block_state.get(FACING).hasDirection(face.getLeftDirection())) return "left";
else if (block_state.get(FACING).hasDirection(face.getRightDirection())) return "right";
}
return "";
}
private static StairShape getPlacementShape(StairDirection face, BlockView world, BlockPos pos) {
StairShape shape = STRAIGHT;
String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos);
switch (sol) {
case "right": return INNER_RIGHT;
case "left": return INNER_LEFT;
}
sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos);
switch (sol) {
case "right": return INNER_RIGHT;
case "left": return INNER_LEFT;
}
sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos);
switch (sol) {
case "right" -> shape = FIRST_OUTER_RIGHT;
case "left" -> shape = FIRST_OUTER_LEFT;
}
sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos);
switch (sol) {
case "right" -> {
if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_RIGHT;
else if (shape.equals(FIRST_OUTER_RIGHT)) shape = OUTER_RIGHT;
}
case "left" -> {
if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_LEFT;
else if (shape.equals(FIRST_OUTER_LEFT)) shape = OUTER_LEFT;
}
}
return shape;
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(TemplateInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
public MultipartBlockStateSupplier getMultipart() {
Identifier straight_id = Templates.id("stairs_special");
Identifier double_outer_id = Templates.id("double_outer_stairs_special");
Identifier inner_id = Templates.id("inner_stairs_special");
Identifier outer_id = Templates.id("outer_stairs_special");
return MultipartBlockStateSupplier.create(this)
/* STRAIGHT X AXIS */
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R0))
.with(GBlockstate.when(FACING, EAST_UP, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R0))
.with(GBlockstate.when(FACING, UP_WEST, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R180))
.with(GBlockstate.when(FACING, WEST_DOWN, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R180))
/* STRAIGHT Y AXIS */
.with(GBlockstate.when(FACING, EAST_SOUTH, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R0))
.with(GBlockstate.when(FACING, SOUTH_WEST, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R90))
.with(GBlockstate.when(FACING, WEST_NORTH, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R180))
.with(GBlockstate.when(FACING, NORTH_EAST, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R270))
/* STRAIGHT Z AXIS */
.with(GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R90))
.with(GBlockstate.when(FACING, NORTH_DOWN, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R270))
.with(GBlockstate.when(FACING, UP_NORTH, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R270))
.with(GBlockstate.when(FACING, SOUTH_UP, SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R90))
/* OUTER X AXIS */
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.variant(outer_id, true, R0, R0))
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, INNER_LEFT),
GBlockstate.variant(inner_id, true, R0, R270))
/* OUTER DOUBLE */
.with(GBlockstate.when(FACING, NORTH_DOWN, SHAPE, OUTER_LEFT),
GBlockstate.variant(double_outer_id, true, R0, R180))
.with(GBlockstate.when(FACING, NORTH_DOWN, SHAPE, OUTER_RIGHT),
GBlockstate.variant(double_outer_id, true, R0, R270));
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return TemplateInteractionUtil.emitsRedstonePower(state);
}
static {
final VoxelShape STRAIGHT = Stream.of(
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),
VoxelShapes.cuboid(0.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f)
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
final VoxelShape JUNCTION = Stream.of(
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),
VoxelShapes.cuboid(0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f)
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
final VoxelShape INNER = Stream.of(
VoxelShapes.cuboid(0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f),
VoxelShapes.cuboid(0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.5f),
VoxelShapes.cuboid(0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f),
VoxelShapes.cuboid(0.5f, 0.0f, 0.5f, 1.0f, 0.5f, 1.0f)
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
final VoxelShape OUTER = Stream.of(
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),
VoxelShapes.cuboid(0.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f),
VoxelShapes.cuboid(0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.5f)
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return TemplateInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
}
VOXEL_LIST.add(STRAIGHT);
VOXEL_LIST.add(VoxelHelper.mirror(STRAIGHT, Axis.Z));
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.X), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.X));
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return TemplateInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y));
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y), Axis.X));
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y), Axis.Z), Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z), Axis.Y));
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z), Axis.Y), Axis.Z));
VOXEL_LIST.add(JUNCTION);
VOXEL_LIST.add(VoxelHelper.rotateClockwise(JUNCTION, Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(JUNCTION, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.Y));
VOXEL_LIST.add(VoxelHelper.mirror(JUNCTION, Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(JUNCTION, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.mirror(JUNCTION, Axis.Y), Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(JUNCTION, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z));
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X));
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X), Axis.X));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X));
VOXEL_LIST.add(INNER);
VOXEL_LIST.add(VoxelHelper.rotateClockwise(INNER, Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(INNER, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(INNER, Axis.Y));
VOXEL_LIST.add(VoxelHelper.mirror(INNER, Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(INNER, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(INNER, Axis.Y), Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(INNER, Axis.Y), Axis.Y));
VOXEL_LIST.add(OUTER);
VOXEL_LIST.add(VoxelHelper.rotateClockwise(OUTER, Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(OUTER, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(OUTER, Axis.Y));
VOXEL_LIST.add(VoxelHelper.mirror(OUTER, Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(OUTER, Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(OUTER, Axis.Y), Axis.Y), Axis.Y));
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(OUTER, Axis.Y), Axis.Y));
}
}

View File

@ -1,6 +1,6 @@
package fr.adrien1106.reframedtemplates.model;
import fr.adrien1106.reframedtemplates.block.TemplateEntity;
import fr.adrien1106.reframedtemplates.block.FramedEntity;
import fr.adrien1106.reframedtemplates.mixin.MinecraftAccessor;
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearance;
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearanceManager;
@ -100,7 +100,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
TemplateAppearance nbtAppearance;
int tint;
BlockState theme = TemplateEntity.readStateFromItem(stack);
BlockState theme = FramedEntity.readStateFromItem(stack);
if(!theme.isAir()) {
nbtAppearance = tam.getTemplateAppearance(theme);
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).templates$getItemColors().getColor(new ItemStack(theme.getBlock()), 0);

View File

@ -0,0 +1,72 @@
package fr.adrien1106.reframedtemplates.util;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
public class VoxelHelper {
public static VoxelShape rotateClockwise(VoxelShape shape, Direction.Axis axis) {
VoxelShape[] buffer = new VoxelShape[]{ shape, VoxelShapes.empty() };
switch (axis) {
case Y:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( 1 - maxZ, minY, minX, 1 - minZ, maxY, maxX), BooleanBiFunction.OR));
break;
case X:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minX, 1 - maxZ, minY, maxX, 1 - minZ, maxY), BooleanBiFunction.OR));
break;
case Z:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( 1 - maxY, minX, minZ, 1 - minY, maxX, maxZ), BooleanBiFunction.OR));
break;
}
return buffer[1];
}
public static VoxelShape rotateCounterClockwise(VoxelShape shape, Direction.Axis axis) {
VoxelShape[] buffer = new VoxelShape[]{ shape, VoxelShapes.empty() };
switch (axis) {
case Y:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minZ, minY, 1 - maxX, maxZ, maxY, 1 - minX), BooleanBiFunction.OR));
break;
case X:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minX, minZ, 1 - maxY, maxX, maxZ, 1 - minY), BooleanBiFunction.OR));
break;
case Z:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minY, 1 - maxX, minZ, maxY, 1 - minX, maxZ), BooleanBiFunction.OR));
break;
}
return buffer[1];
}
public static VoxelShape mirror(VoxelShape shape, Direction.Axis axis) {
VoxelShape[] buffer = new VoxelShape[]{ shape, VoxelShapes.empty() };
switch (axis) {
case Y:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minX, 1 - maxY, minZ, maxX, 1 - minY, maxZ), BooleanBiFunction.OR));
break;
case X:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( 1 - maxX, minY, minZ, 1 - minX, maxY, maxZ), BooleanBiFunction.OR));
break;
case Z:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minX, minY, 1 - maxZ, maxX, maxY, 1 - minZ), BooleanBiFunction.OR));
break;
}
return buffer[1];
}
public static VoxelShape offset(VoxelShape shape, Direction.Axis axis, float offset) {
VoxelShape[] buffer = new VoxelShape[]{ shape, VoxelShapes.empty() };
switch (axis) {
case Y:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minX, offset + minY, minZ, maxX, offset + maxY, maxZ), BooleanBiFunction.OR));
break;
case X:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( offset + minX, minY, minZ, offset + maxX, maxY, maxZ), BooleanBiFunction.OR));
break;
case Z:
buffer[0].forEachBox((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = VoxelShapes.combineAndSimplify( buffer[1], VoxelShapes.cuboid( minX, minY, offset + minZ, maxX, maxY, offset + maxZ), BooleanBiFunction.OR));
break;
}
return buffer[1];
}
}

View File

@ -0,0 +1,87 @@
package fr.adrien1106.reframedtemplates.util.property;
import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.math.Direction;
import java.util.Arrays;
import java.util.Map;
public enum StairDirection implements StringIdentifiable {
NORTH_DOWN("north_down", Direction.NORTH, Direction.DOWN, Direction.EAST, 0),
DOWN_SOUTH("down_south", Direction.DOWN, Direction.SOUTH, Direction.EAST, 1),
SOUTH_UP("south_up", Direction.SOUTH, Direction.UP, Direction.EAST, 2),
UP_NORTH("up_north", Direction.UP, Direction.NORTH, Direction.EAST, 3),
WEST_DOWN("west_down", Direction.WEST, Direction.DOWN, Direction.SOUTH, 4),
DOWN_EAST("down_east", Direction.DOWN, Direction.EAST, Direction.SOUTH, 5),
EAST_UP("east_up", Direction.EAST, Direction.UP, Direction.SOUTH, 6),
UP_WEST("up_west", Direction.UP, Direction.WEST, Direction.SOUTH, 7),
WEST_NORTH("west_north", Direction.WEST, Direction.NORTH, Direction.DOWN, 8),
NORTH_EAST("north_east", Direction.NORTH, Direction.EAST, Direction.DOWN, 9),
EAST_SOUTH("east_south", Direction.EAST, Direction.SOUTH, Direction.DOWN, 10),
SOUTH_WEST("south_west", Direction.SOUTH, Direction.WEST, Direction.DOWN, 11);
private final String name;
private final Direction first_direction;
private final Direction second_direction;
private final Direction right_direction;
private final Direction left_direction;
private final int ID;
StairDirection(String name, Direction first_direction, Direction second_direction, Direction right_direction, int id) {
this.name = name;
this.first_direction = first_direction;
this.second_direction = second_direction;
this.right_direction = right_direction;
this.left_direction = right_direction.getOpposite();
this.ID = id;
}
public String asString() {
return this.name;
}
public String toString() {
return asString();
}
public Direction getFirstDirection() {
return first_direction;
}
public Direction getSecondDirection() {
return second_direction;
}
public Direction getRightDirection() {
return right_direction;
}
public Direction getLeftDirection() {
return left_direction;
}
public boolean hasDirection(Direction direction) {
return this.first_direction.equals(direction)
|| this.second_direction.equals(direction);
}
public int getID() {
return this.ID;
}
public static StairDirection getByDirections(Direction direction_1, Direction direction_2) {
return Arrays.stream(StairDirection.values())
.filter(value -> value.hasDirection(direction_1) && value.hasDirection(direction_2))
.findFirst().orElse(StairDirection.NORTH_DOWN);
}
public static StairDirection fromId(int id) {
return Arrays.stream(StairDirection.values())
.filter(value -> value.getID() == id)
.findFirst().orElse(StairDirection.NORTH_DOWN);
}
public static StairDirection fromName(String name) {
return Arrays.stream(StairDirection.values())
.filter(value -> value.name().equals(name))
.findFirst().orElse(StairDirection.NORTH_DOWN);
}
}

View File

@ -0,0 +1,51 @@
package fr.adrien1106.reframedtemplates.util.property;
import net.minecraft.util.StringIdentifiable;
import java.util.Arrays;
public enum StairShape implements StringIdentifiable {
STRAIGHT("straight", 0),
INNER_RIGHT("inner_right", 1),
INNER_LEFT("inner_left", 2),
OUTER_RIGHT("outer_right", 3),
OUTER_LEFT("outer_left", 4),
FIRST_OUTER_RIGHT("first_outer_right", 5),
FIRST_OUTER_LEFT("first_outer_left", 6),
SECOND_OUTER_RIGHT("second_outer_right", 7),
SECOND_OUTER_LEFT("second_outer_left", 8);
private final String name;
private final int ID;
StairShape(String name, int id) {
this.name = name;
this.ID = id;
}
public String asString() {
return this.name;
}
public String toString() {
return asString();
}
public int getID() {
return this.ID;
}
public static StairShape fromId(int id) {
return Arrays.stream(StairShape.values())
.filter(value -> value.getID() == id)
.findFirst().orElse(StairShape.STRAIGHT);
}
public static StairShape fromName(String name) {
return Arrays.stream(StairShape.values())
.filter(value -> value.name().equals(name))
.findFirst().orElse(StairShape.STRAIGHT);
}
}

View File

@ -0,0 +1,54 @@
{
"parent": "minecraft:block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 8, 16],
"faces": {
"north": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "north"},
"east": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "east"},
"south": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "south"},
"west": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "west"},
"up": {"uv": [0, 0, 16, 16], "texture": "#top"},
"down": {"uv": [0, 0, 16, 16], "texture": "#bottom", "cullface": "down"}
}
},
{
"from": [8, 0, 0],
"to": [16, 8, 16],
"faces": {
"north": {"uv": [0, 8, 8, 16], "texture": "#side", "cullface": "north"},
"east": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "east"},
"south": {"uv": [8, 8, 16, 16], "texture": "#side", "cullface": "south"},
"west": {"uv": [0, 8, 16, 16], "texture": "#side"},
"up": {"uv": [8, 0, 16, 16], "texture": "#top", "cullface": "up"},
"down": {"uv": [8, 0, 16, 16], "texture": "#bottom"}
}
},
{
"from": [8, 8, 8],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 8, 8], "texture": "#side"},
"east": {"uv": [0, 0, 8, 8], "texture": "#side"},
"south": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "south"},
"west": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "west"},
"up": {"uv": [8, 8, 16, 16], "texture": "#top", "cullface": "up"}
}
},
{
"from": [0, 0, 8],
"to": [8, 8, 16],
"faces": {
"north": {"uv": [8, 8, 16, 16], "texture": "#side"},
"south": {"uv": [0, 8, 8, 16], "texture": "#side", "cullface": "south"},
"west": {"uv": [8, 8, 16, 16], "texture": "#side", "cullface": "west"},
"up": {"uv": [0, 8, 8, 16], "texture": "#top", "cullface": "up"},
"down": {"uv": [0, 0, 8, 8], "texture": "#bottom"}
}
}
]
}