package mekanism.client.render.ctm; import static net.minecraft.util.EnumFacing.DOWN; import static net.minecraft.util.EnumFacing.EAST; import static net.minecraft.util.EnumFacing.NORTH; import static net.minecraft.util.EnumFacing.SOUTH; import static net.minecraft.util.EnumFacing.UP; import static net.minecraft.util.EnumFacing.WEST; import net.minecraft.block.state.IBlockState; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.Axis; import net.minecraft.util.EnumFacing.AxisDirection; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; /** * Think of this class as a "Two dimensional ForgeDirection, with diagonals". * <p> * It represents the eight different directions a face of a block can connect with CTM, and contains the logic for determining if a block is indeed connected in that direction. * <p> * Note that, for example, {@link #TOP_RIGHT} does not mean connected to the {@link #TOP} and {@link #RIGHT}, but connected in the diagonal direction represented by {@link #TOP_RIGHT}. This is used * for inner corner rendering. */ public enum Dir { // @formatter:off TOP(UP), TOP_RIGHT(UP, EAST), RIGHT(EAST), BOTTOM_RIGHT(DOWN, EAST), BOTTOM(DOWN), BOTTOM_LEFT(DOWN, WEST), LEFT(WEST), TOP_LEFT(UP, WEST); // @formatter:on /** * All values of this enum, used to prevent unnecessary allocation via {@link #values()}. */ public static final Dir[] VALUES = values(); private static final EnumFacing NORMAL = SOUTH; private EnumFacing[] dirs; private Dir(EnumFacing... dirs) { this.dirs = dirs; } /** * Finds if this block is connected for the given side in this Dir. * * @param inst * The CTM instance to use for logic. * @param world * The world the block is in. * @param pos * The position of your block. * @param side * The side of the current face. * @return True if the block is connected in the given Dir, false otherwise. */ public boolean isConnected(CTM ctm, IBlockAccess world, BlockPos pos, EnumFacing side) { return ctm.isConnected(world, pos, getConnection(pos, side), side); } /** * Finds if this block is connected for the given side in this Dir. * * @param inst * The CTM instance to use for logic. * @param world * The world the block is in. * @param pos * The position of your block. * @param side * The side of the current face. * @param side * The state to check for connection with. * @return True if the block is connected in the given Dir, false otherwise. */ public boolean isConnected(CTM ctm, IBlockAccess world, BlockPos pos, EnumFacing side, IBlockState state) { return ctm.isConnected(world, pos, getConnection(pos, side), side, state); } private BlockPos getConnection(BlockPos pos, EnumFacing side) { EnumFacing[] dirs = getNormalizedDirs(side); BlockPos connection = pos; for (EnumFacing dir : dirs) { connection = connection.offset(dir); } return connection; } public EnumFacing[] getNormalizedDirs(EnumFacing normal) { if (normal == NORMAL) { return dirs; } else if (normal == NORMAL.getOpposite()) { // If this is the opposite direction of the default normal, we // need to mirror the dirs // A mirror version does not affect y+ and y- so we ignore those EnumFacing[] ret = new EnumFacing[dirs.length]; for (int i = 0; i < ret.length; i++) { ret[i] = dirs[i].getFrontOffsetY() != 0 ? dirs[i] : dirs[i].getOpposite(); } return ret; } else { EnumFacing axis = null; // Next, we need different a different rotation axis depending // on if this is up/down or not if (normal.getFrontOffsetY() == 0) { // If it is not up/down, pick either the left or right-hand // rotation axis = normal == NORMAL.rotateY() ? UP : DOWN; } else { // If it is up/down, pick either the up or down rotation. axis = normal == UP ? NORMAL.rotateYCCW() : NORMAL.rotateY(); } EnumFacing[] ret = new EnumFacing[dirs.length]; // Finally apply all the rotations for (int i = 0; i < ret.length; i++) { ret[i] = rotate(dirs[i], axis); } return ret; } } // God why private static final int[] FACING_LOOKUP = new int[EnumFacing.values().length]; static { FACING_LOOKUP[NORTH.ordinal()] = 1; FACING_LOOKUP[EAST.ordinal()] = 2; FACING_LOOKUP[SOUTH.ordinal()] = 3; FACING_LOOKUP[WEST.ordinal()] = 4; FACING_LOOKUP[UP.ordinal()] = 5; FACING_LOOKUP[DOWN.ordinal()] = 6; } private EnumFacing rotate(EnumFacing facing, EnumFacing axisFacing) { Axis axis = axisFacing.getAxis(); AxisDirection axisDir = axisFacing.getAxisDirection(); if (axisDir == AxisDirection.POSITIVE) { return facing.rotateAround(axis); } if (facing.getAxis() != axis) { switch (axis) { case X: // I did some manual testing and this is what worked...I don't get it either switch (FACING_LOOKUP[facing.ordinal()]) { case 1: return NORTH; case 2: case 4: default: return facing; // Invalid but ignored case 3: return SOUTH; case 5: return SOUTH; case 6: return NORTH; } case Y: return facing.rotateYCCW(); case Z: switch (FACING_LOOKUP[facing.ordinal()]) { case 2: return EAST; case 3: default: return facing; // invalid but ignored case 4: return WEST; case 5: return DOWN; case 6: return UP; } } } return facing; } }