package codechicken.lib.vec; import codechicken.lib.math.MathHelper; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; public class Rotation extends Transformation { /** * Clockwise pi/2 about y looking down */ public static Transformation[] quarterRotations = new Transformation[] { new RedundantTransformation(), new VariableTransformation(new Matrix4(0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { double d1 = vec.x; double d2 = vec.z; vec.x = -d2; vec.z = d1; } @Override public Transformation inverse() { return quarterRotations[3]; } }, new VariableTransformation(new Matrix4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { vec.x = -vec.x; vec.z = -vec.z; } @Override public Transformation inverse() { return this; } }, new VariableTransformation(new Matrix4(0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { double d1 = vec.x; double d2 = vec.z; vec.x = d2; vec.z = -d1; } @Override public Transformation inverse() { return quarterRotations[1]; } } }; public static Transformation[] sideRotations = new Transformation[] { new RedundantTransformation(), new VariableTransformation(new Matrix4(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { vec.y = -vec.y; vec.z = -vec.z; } @Override public Transformation inverse() { return this; } }, new VariableTransformation(new Matrix4(1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { double d1 = vec.y; double d2 = vec.z; vec.y = -d2; vec.z = d1; } @Override public Transformation inverse() { return sideRotations[3]; } }, new VariableTransformation(new Matrix4(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { double d1 = vec.y; double d2 = vec.z; vec.y = d2; vec.z = -d1; } @Override public Transformation inverse() { return sideRotations[2]; } }, new VariableTransformation(new Matrix4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { double d0 = vec.x; double d1 = vec.y; vec.x = d1; vec.y = -d0; } @Override public Transformation inverse() { return sideRotations[5]; } }, new VariableTransformation(new Matrix4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) { @Override public void apply(Vector3 vec) { double d0 = vec.x; double d1 = vec.y; vec.x = -d1; vec.y = d0; } @Override public Transformation inverse() { return sideRotations[4]; } } }; public static Vector3[] axes = new Vector3[] { new Vector3(0, -1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1), new Vector3(0, 0, 1), new Vector3(-1, 0, 0), new Vector3(1, 0, 0) }; public static int[] sideRotMap = new int[] { 3, 4, 2, 5, 3, 5, 2, 4, 1, 5, 0, 4, 1, 4, 0, 5, 1, 2, 0, 3, 1, 3, 0, 2 }; public static int[] rotSideMap = new int[] { -1, -1, 2, 0, 1, 3, -1, -1, 2, 0, 3, 1, 2, 0, -1, -1, 3, 1, 2, 0, -1, -1, 1, 3, 2, 0, 1, 3, -1, -1, 2, 0, 3, 1, -1, -1 }; /** * Rotate pi/2 * this offset for [side] about y axis before rotating to the side for the rotation indicies to line up */ public static int[] sideRotOffsets = new int[] { 0, 2, 2, 0, 1, 3 }; public static int rotateSide(int s, int r) { return sideRotMap[s << 2 | r]; } /** * Reverse of rotateSide */ public static int rotationTo(int s1, int s2) { if ((s1 & 6) == (s2 & 6)) { throw new IllegalArgumentException("Faces " + s1 + " and " + s2 + " are opposites"); } return rotSideMap[s1 * 6 + s2]; } /** * @param player The placing player, used for obtaining the look vector * @param side The side of the block being placed on * @return The rotation for the face == side^1 */ public static int getSidedRotation(EntityPlayer player, int side) { Vector3 look = new Vector3(player.getLook(1)); double max = 0; int maxr = 0; for (int r = 0; r < 4; r++) { Vector3 axis = Rotation.axes[rotateSide(side ^ 1, r)]; double d = look.scalarProject(axis); if (d > max) { max = d; maxr = r; } } return maxr; } /** * @return The rotation quat for side 0 and rotation 0 to side s with rotation r */ public static Transformation sideOrientation(int s, int r) { return quarterRotations[(r + sideRotOffsets[s]) % 4].with(sideRotations[s]); } /** * @param entity The placing entity, used for obtaining the look vector * @return The side towards which the entity is most directly looking. */ public static int getSideFromLookAngle(EntityLivingBase entity) { Vector3 look = new Vector3(entity.getLook(1)); double max = 0; int maxs = 0; for (int s = 0; s < 6; s++) { double d = look.scalarProject(axes[s]); if (d > max) { max = d; maxs = s; } } return maxs; } public double angle; public Vector3 axis; private Quat quat; public Rotation(double angle, Vector3 axis) { this.angle = angle; this.axis = axis; } public Rotation(double angle, double x, double y, double z) { this(angle, new Vector3(x, y, z)); } public Rotation(Quat quat) { this.quat = quat; angle = Math.acos(quat.s) * 2; if (angle == 0) { axis = new Vector3(0, 1, 0); } else { double sa = Math.sin(angle * 0.5); axis = new Vector3(quat.x / sa, quat.y / sa, quat.z / sa); } } @Override public void apply(Vector3 vec) { if (quat == null) { quat = Quat.aroundAxis(axis, angle); } vec.rotate(quat); } @Override public void applyN(Vector3 normal) { apply(normal); } @Override public void apply(Matrix4 mat) { mat.rotate(angle, axis); } public Quat toQuat() { if (quat == null) { quat = Quat.aroundAxis(axis, angle); } return quat; } @Override @SideOnly(Side.CLIENT) public void glApply() { GlStateManager.rotate((float) (angle * MathHelper.todeg), (float) axis.x, (float) axis.y, (float) axis.z); } @Override public Transformation inverse() { return new Rotation(-angle, axis); } @Override public Transformation merge(Transformation next) { if (next instanceof Rotation) { Rotation r = (Rotation) next; if (r.axis.equalsT(axis)) { return new Rotation(angle + r.angle, axis); } return new Rotation(toQuat().copy().multiply(r.toQuat())); } return null; } @Override public boolean isRedundant() { return MathHelper.between(-1E-5, angle, 1E-5); } @Override public String toString() { MathContext cont = new MathContext(4, RoundingMode.HALF_UP); return "Rotation(" + new BigDecimal(angle, cont) + ", " + new BigDecimal(axis.x, cont) + ", " + new BigDecimal(axis.y, cont) + ", " + new BigDecimal(axis.z, cont) + ")"; } }