package openmods.block; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import java.util.Set; import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.common.util.ForgeDirection; import openmods.geometry.BlockTextureTransform; import openmods.geometry.HalfAxis; import openmods.geometry.Orientation; import openmods.renderer.rotations.IRendererSetup; import openmods.renderer.rotations.RendererSetupProxy; import openmods.utils.BlockUtils; public enum BlockRotationMode { /** * No rotations - always oriented by world directions */ NONE(RotationAxis.NO_AXIS) { @Override public boolean isPlacementValid(Orientation dir) { return true; } @Override public Orientation fromValue(int value) { return Orientation.XP_YP; } @Override public int toValue(Orientation dir) { return 0; } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return Orientation.XP_YP; } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { return Orientation.XP_YP; } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { return null; } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.NULL; } }, /** * Two orientations - either N-S or W-E. Top side remains unchanged. * Placement side will become local north or south. * Tool rotation will either rotate around Y (if clicked T or B) or set to clicked side (otherwise). */ TWO_DIRECTIONS(RotationAxis.THREE_AXIS, Orientation.ZN_YP, Orientation.XP_YP) { private Orientation directionToOrientation(final ForgeDirection normalDir) { switch (normalDir) { case EAST: case WEST: return Orientation.ZN_YP; case NORTH: case SOUTH: return Orientation.XP_YP; default: return null; } } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return directionToOrientation(side); } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { final ForgeDirection playerOrientation = BlockUtils.get2dOrientation(player); return directionToOrientation(playerOrientation); } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { switch (axis) { case UP: case DOWN: return (currentOrientation == Orientation.ZN_YP)? Orientation.XP_YP : Orientation.ZN_YP; case NORTH: case SOUTH: case EAST: case WEST: return directionToOrientation(axis); default: return null; } } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.proxy.getVanillaRenderer(); } }, /** * Three orientations: N-S, W-E, T-B. * Placement side will become local top or bottom. * Tool rotation will set top direction to clicked side. */ THREE_DIRECTIONS(RotationAxis.THREE_AXIS, Orientation.XP_YP, Orientation.YP_XN, Orientation.XP_ZN) { private Orientation directionToOrientation(ForgeDirection dir) { switch (dir) { case EAST: case WEST: return Orientation.YP_XN; case NORTH: case SOUTH: return Orientation.XP_ZN; case UP: case DOWN: default: return Orientation.XP_YP; } } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return directionToOrientation(side); } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { final ForgeDirection normalDir = BlockUtils.get3dOrientation(player); return directionToOrientation(normalDir); } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { return directionToOrientation(axis); } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST).mirrorU(ForgeDirection.DOWN); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.proxy.getTweakedRenderer(); } }, /** * Rotate around Y in for directions: N,S,W,E. * Placement side will become local north. * Tool rotation will either rotate around Y (if clicked T or B) or set to clicked side (otherwise). */ FOUR_DIRECTIONS(RotationAxis.THREE_AXIS, Orientation.XP_YP, Orientation.ZN_YP, Orientation.XN_YP, Orientation.ZP_YP) { private Orientation directionToOrientation(ForgeDirection side) { switch (side) { case SOUTH: return Orientation.XP_YP; case WEST: return Orientation.ZP_YP; case NORTH: return Orientation.XN_YP; case EAST: return Orientation.ZN_YP; default: return null; } } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return directionToOrientation(side); } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { final ForgeDirection side = BlockUtils.get2dOrientation(player).getOpposite(); return directionToOrientation(side); } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { switch (axis) { case UP: return currentOrientation.rotateAround(HalfAxis.POS_Y); case DOWN: return currentOrientation.rotateAround(HalfAxis.NEG_Y); case NORTH: case SOUTH: case EAST: case WEST: return directionToOrientation(axis); default: return null; } } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.proxy.getVanillaRenderer(); } @Override public Orientation getInventoryRenderOrientation() { return Orientation.XN_YP; } }, /** * Rotations in every direction. * Placement side will become local top. * Tool rotation will set top to clicked side. */ SIX_DIRECTIONS(RotationAxis.THREE_AXIS, Orientation.XN_YN, Orientation.XP_YP, Orientation.XP_ZN, Orientation.XP_ZP, Orientation.YP_XN, Orientation.YN_XP) { public Orientation directionToOrientation(ForgeDirection localTop) { switch (localTop) { case DOWN: return Orientation.XN_YN; case EAST: return Orientation.YN_XP; case NORTH: return Orientation.XP_ZN; case SOUTH: return Orientation.XP_ZP; case WEST: return Orientation.YP_XN; case UP: default: return Orientation.XP_YP; } } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return directionToOrientation(side); } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { final ForgeDirection localTop = BlockUtils.get3dOrientation(player).getOpposite(); return directionToOrientation(localTop); } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { return directionToOrientation(axis); } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST).mirrorU(ForgeDirection.DOWN); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.proxy.getTweakedRenderer(); } @Override public Orientation getInventoryRenderOrientation() { return Orientation.YN_XP; } }, /** * And now it's time for weird ones... * Three orientations: N-S, W-E, T-B. * Placement side will become local top or bottom. * Side can be rotated in four directions */ THREE_FOUR_DIRECTIONS(RotationAxis.THREE_AXIS, Orientation.XP_YP, Orientation.XN_YP, Orientation.ZP_YP, Orientation.ZN_YP, Orientation.YP_XN, Orientation.YN_XN, Orientation.ZP_XN, Orientation.ZN_XN, Orientation.XP_ZN, Orientation.XN_ZN, Orientation.YP_ZN, Orientation.YN_ZN) { private Orientation directionToOrientation(ForgeDirection dir) { switch (dir) { case EAST: case WEST: return Orientation.YP_XN; case NORTH: case SOUTH: return Orientation.XP_ZN; case UP: case DOWN: default: return Orientation.XP_YP; } } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return directionToOrientation(side); } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { final ForgeDirection normalDir = BlockUtils.get3dOrientation(player); return directionToOrientation(normalDir); } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { final HalfAxis newTop = HalfAxis.fromDirection(axis); final HalfAxis currentTop = currentOrientation.y; if (newTop == currentTop) { return currentOrientation.rotateAround(HalfAxis.POS_Y); } else if (newTop == currentTop.negate()) { return currentOrientation.rotateAround(HalfAxis.NEG_Y); } else { return directionToOrientation(axis); } } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST).mirrorU(ForgeDirection.DOWN); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.proxy.getTweakedRenderer(); } @Override public Orientation getInventoryRenderOrientation() { return Orientation.XN_YP; } }, /** * Yet another weird one. * Top side can rotate when oriented up or down. * When top points to cardinal direction, texture top should always align with horizon */ TWELVE_DIRECTIONS(RotationAxis.THREE_AXIS, Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.NEG_Z), // first two TOP/BOTTOM orientation are here for easy migration from SIX_DIRECTIONS Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.POS_Z), Orientation.lookupYZ(HalfAxis.NEG_Z, HalfAxis.NEG_Y), Orientation.lookupYZ(HalfAxis.POS_Z, HalfAxis.NEG_Y), Orientation.lookupYZ(HalfAxis.NEG_X, HalfAxis.NEG_Y), Orientation.lookupYZ(HalfAxis.POS_X, HalfAxis.NEG_Y), Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.POS_Z), Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.NEG_X), Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.POS_X), Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.NEG_Z), Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.POS_X), Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.NEG_X)) { public Orientation directionToOrientation(ForgeDirection localTop) { switch (localTop) { case DOWN: return Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.NEG_Z); case EAST: return Orientation.lookupYZ(HalfAxis.POS_X, HalfAxis.NEG_Y); case NORTH: return Orientation.lookupYZ(HalfAxis.NEG_Z, HalfAxis.NEG_Y); case SOUTH: return Orientation.lookupYZ(HalfAxis.POS_Z, HalfAxis.NEG_Y); case WEST: return Orientation.lookupYZ(HalfAxis.NEG_X, HalfAxis.NEG_Y); case UP: default: return Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.POS_Z); } } @Override public Orientation getPlacementOrientationFromSurface(ForgeDirection side) { return directionToOrientation(side); } @Override public Orientation getPlacementOrientationFromEntity(EntityPlayer player) { ForgeDirection playerDir = BlockUtils.get2dOrientation(player).getOpposite(); if (player.rotationPitch > 45.5F) { return Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.fromDirection(playerDir)); } else if (player.rotationPitch < -45.5F) { return Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.fromDirection(playerDir)); } else { return Orientation.lookupYZ(HalfAxis.fromDirection(playerDir), HalfAxis.NEG_Y); } } @Override public Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis) { switch (axis) { case NORTH: case SOUTH: case EAST: case WEST: return Orientation.lookupYZ(HalfAxis.fromDirection(axis), HalfAxis.NEG_Y); case UP: if (currentOrientation.y != HalfAxis.POS_Y) return Orientation.lookupYZ(HalfAxis.POS_Y, HalfAxis.POS_Z); else return currentOrientation.rotateAround(HalfAxis.POS_Y); case DOWN: if (currentOrientation.y != HalfAxis.NEG_Y) return Orientation.lookupYZ(HalfAxis.NEG_Y, HalfAxis.NEG_Z); else return currentOrientation.rotateAround(HalfAxis.POS_Y); default: return null; } } @Override protected BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder) { return builder.mirrorU(ForgeDirection.NORTH).mirrorU(ForgeDirection.EAST).mirrorU(ForgeDirection.DOWN); } @Override public IRendererSetup getRenderSetup() { return RendererSetupProxy.proxy.getTweakedRenderer(); } }; private static final int MAX_ORIENTATIONS = 16; private BlockRotationMode(ForgeDirection[] rotations, Orientation... validOrientations) { this.rotationAxes = rotations; this.validDirections = ImmutableSet.copyOf(validOrientations); final int count = validOrientations.length; Preconditions.checkArgument(this.validDirections.size() == count, "Duplicated directions"); Preconditions.checkArgument(count <= MAX_ORIENTATIONS, "Too many values: %s", count); this.idToOrientation = new Orientation[MAX_ORIENTATIONS]; this.orientationToId = new int[Orientation.VALUES.length]; for (int i = 0; i < count; i++) { final Orientation orientation = validOrientations[i]; Preconditions.checkNotNull(orientation); idToOrientation[i] = orientation; orientationToId[orientation.ordinal()] = i; } if (count == 0) { this.bitCount = 0; this.mask = 0; } else { final int maxValue = count - 1; this.bitCount = Integer.SIZE - Integer.numberOfLeadingZeros(maxValue); this.mask = (1 << bitCount) - 1; for (int i = count; i < idToOrientation.length; i++) idToOrientation[i] = idToOrientation[0]; } this.textureTransform = setupTextureTransform(BlockTextureTransform.builder()).build(); } private final Orientation[] idToOrientation; private final int[] orientationToId; public final ForgeDirection[] rotationAxes; public final Set<Orientation> validDirections; public final int bitCount; public final int mask; public final BlockTextureTransform textureTransform; protected abstract BlockTextureTransform.Builder setupTextureTransform(BlockTextureTransform.Builder builder); public abstract IRendererSetup getRenderSetup(); public Orientation fromValue(int value) { try { return idToOrientation[value]; } catch (IndexOutOfBoundsException e) { return idToOrientation[0]; } } public int toValue(Orientation dir) { try { return orientationToId[dir.ordinal()]; } catch (IndexOutOfBoundsException e) { return 0; } } public boolean isPlacementValid(Orientation dir) { return validDirections.contains(dir); } public abstract Orientation getPlacementOrientationFromSurface(ForgeDirection side); public abstract Orientation getPlacementOrientationFromEntity(EntityPlayer player); public abstract Orientation calculateToolRotation(Orientation currentOrientation, ForgeDirection axis); public Orientation getInventoryRenderOrientation() { return Orientation.XP_YP; } }