public class ModelVRHead extends ModelBase {
	public ModelRenderer head;
	public ModelRenderer headwear;
	
	public ModelVRHead() {
		this(0);
	}
	
	public ModelVRHead(float scaleFactor) {
		head = new ModelRenderer(this, 0, 0);
		head.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, scaleFactor);
		head.setRotationPoint(0, 0, 0);
		headwear = new ModelRenderer(this, 32, 0);
		headwear.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, scaleFactor + 0.5F);
		headwear.setRotationPoint(0, 0, 0);
	}

	/**
	 * lol coords are not used here so don't even try
	 */
	@Override
	public void render(Entity entity, float x, float y, float z, float yaw, float pitch, float whatIsThis) {
		head.render(whatIsThis);
		headwear.render(whatIsThis);
	}
} public abstract class EntityVRArm extends EntityVRObject {
	public boolean mirror;
	
	public EntityVRArm(World world) {
		super(world);
	}

	public EntityVRArm(World world, int associatedEntityId) {
		super(world, associatedEntityId);
	}

	@Override
	protected void entityInit() {
		super.entityInit();
		dataWatcher.addObject(9, (byte)0);
	}

	@Override
	public void onUpdate() {
		super.onUpdate();
		if (!worldObj.isRemote) {
			dataWatcher.updateObject(9, mirror ? (byte)1 : (byte)0);
		}
		else {
			mirror = dataWatcher.getWatchableObjectByte(9) != 0;
		}
	}

	@Override
	public void writeSpawnData(ByteBuf buf) {
		super.writeSpawnData(buf);
		buf.writeBoolean(mirror);
	}

	@Override
	public void readSpawnData(ByteBuf buf) {
		super.readSpawnData(buf);
		mirror = buf.readBoolean();
	}
} public class EntityVRHead extends EntityVRObject {
	public EntityVRHead(World world) {
		super(world);
	}

	public EntityVRHead(World world, int associatedEntityId) {
		super(world, associatedEntityId);
	}
} public class EntityVROffHandArm extends EntityVRArm {
	public EntityVROffHandArm(World world) {
		super(world);
	}

	public EntityVROffHandArm(World world, int associatedEntityId) {
		super(world, associatedEntityId);
	}
} public abstract class EntityVRObject extends Entity implements IEntityAdditionalSpawnData {
	public Vec3 position = Vec3.createVectorHelper(0, 0, 0);
	public Vec3 positionLast = Vec3.createVectorHelper(0, 0, 0);
	public float rotW = 1, rotX, rotY, rotZ;
	public float rotWLast = 1, rotXLast, rotYLast, rotZLast;
	protected int associatedEntityId;
	protected EntityPlayer entityPlayer;
	private boolean spawned;
	
	public EntityVRObject(World world) {
		this(world, -1);
	}
	
	public EntityVRObject(World world, int associatedEntityId) {
		super(world);
		this.associatedEntityId = associatedEntityId;
		this.ignoreFrustumCheck = true;
		this.renderDistanceWeight = 10.0D;
	}

	@Override
	protected void entityInit() {
		dataWatcher.addObject(2, 0F);
		dataWatcher.addObject(3, 0F);
		dataWatcher.addObject(4, 0F);
		dataWatcher.addObject(5, 0F);
		dataWatcher.addObject(6, 0F);
		dataWatcher.addObject(7, 0F);
		dataWatcher.addObject(8, 0F);
	}
	
	@Override
	public void onUpdate() {
		if (!worldObj.isRemote) {
			if (getEntityPlayer() == null || getEntityPlayer().isDead) {
				this.setDead();
				return;
			}
			dataWatcher.updateObject(2, (float)position.xCoord);
			dataWatcher.updateObject(3, (float)position.yCoord);
			dataWatcher.updateObject(4, (float)position.zCoord);
			dataWatcher.updateObject(5, (float)rotW);
			dataWatcher.updateObject(6, (float)rotX);
			dataWatcher.updateObject(7, (float)rotY);
			dataWatcher.updateObject(8, (float)rotZ);
		}
		else {
			positionLast.xCoord = position.xCoord;
			positionLast.yCoord = position.yCoord;
			positionLast.zCoord = position.zCoord;
			rotWLast = rotW;
			rotXLast = rotX;
			rotYLast = rotY;
			rotZLast = rotZ;
			position.xCoord = dataWatcher.getWatchableObjectFloat(2);
			position.yCoord = dataWatcher.getWatchableObjectFloat(3);
			position.zCoord = dataWatcher.getWatchableObjectFloat(4);
			rotW = dataWatcher.getWatchableObjectFloat(5);
			rotX = dataWatcher.getWatchableObjectFloat(6);
			rotY = dataWatcher.getWatchableObjectFloat(7);
			rotZ = dataWatcher.getWatchableObjectFloat(8);
		}
	}

	@Override
	protected void readEntityFromNBT(NBTTagCompound p_70037_1_) {
	}

	@Override
	protected void writeEntityToNBT(NBTTagCompound p_70014_1_) {
	}
	
	@Override
	public void writeSpawnData(ByteBuf buf) {
		buf.writeInt(associatedEntityId);
		buf.writeFloat((float)position.xCoord);
		buf.writeFloat((float)position.yCoord);
		buf.writeFloat((float)position.zCoord);
		buf.writeFloat((float)rotW);
		buf.writeFloat((float)rotX);
		buf.writeFloat((float)rotY);
		buf.writeFloat((float)rotZ);
	}

	@Override
	public void readSpawnData(ByteBuf buf) {
		associatedEntityId = buf.readInt();
		position.xCoord = positionLast.xCoord = buf.readFloat();
		position.yCoord = positionLast.yCoord = buf.readFloat();
		position.zCoord = positionLast.zCoord = buf.readFloat();
		rotW = rotWLast = buf.readFloat();
		rotX = rotXLast = buf.readFloat();
		rotY = rotYLast = buf.readFloat();
		rotZ = rotZLast = buf.readFloat();
	}
	
	@Override
	public void setPosition(double x, double y, double z) {
		this.posX = x;
		this.posY = y;
		this.posZ = z;
	}

	@SideOnly(Side.CLIENT)
	public boolean isInRangeToRenderDist(double dist) {
		return true;
	}

	public EntityPlayer getEntityPlayer() {
		if (entityPlayer == null) {
			entityPlayer = (EntityPlayer)worldObj.getEntityByID(associatedEntityId);
		}
		return entityPlayer;
	}
	
	public boolean isSpawned() {
		return spawned;
	}
	
	public void setSpawned() {
		spawned = true;
	}
	
	@SideOnly(Side.CLIENT)
	public Quaternion getRotation() {
		return new Quaternion(rotW, rotX, rotY, rotZ);
	}
	
	@SideOnly(Side.CLIENT)
	public Quaternion getRotationLast() {
		return new Quaternion(rotWLast, rotXLast, rotYLast, rotZLast);
	}
} public class EntityVRMainArm extends EntityVRArm {
	public EntityVRMainArm(World world) {
		super(world);
	}

	public EntityVRMainArm(World world, int associatedEntityId) {
		super(world, associatedEntityId);
	}
} public class ProxyClient extends ProxyCommon {
	public static boolean isVFEServer;
	public static boolean reverseHandsLast;
	public static float worldScaleLast;
	public static boolean seatedLast;
	public static Map<Integer, VRPlayerData> vrPlayerIds = new HashMap<Integer, VRPlayerData>();

	@Override
	public void registerRenderers() {
		this.registerEntityRenderers();
	}

	@Override
	public void registerEventHandlers() {
		super.registerEventHandlers();
		MinecraftForge.EVENT_BUS.register(new HandlerRenderEvent());
		FMLCommonHandler.instance().bus().register(new HandlerClientTick());
	}

	@Override
	public EntityPlayer getPlayerFromNetHandler(INetHandler netHandler) {
		if (netHandler instanceof NetHandlerPlayServer) {
			return ((NetHandlerPlayServer)netHandler).playerEntity;
		}
		return FMLClientHandler.instance().getClientPlayerEntity();
	}

	private void registerEntityRenderers() {
		RenderingRegistry.registerEntityRenderingHandler(EntityVRHead.class, new RenderEntityVRHead());
		RenderingRegistry.registerEntityRenderingHandler(EntityVRMainArm.class, new RenderEntityVRMainArm());
		RenderingRegistry.registerEntityRenderingHandler(EntityVROffHandArm.class, new RenderEntityVROffHandArm());
	}

	public static boolean isVRPlayer(EntityPlayer entity) {
		return entity != Minecraft.getMinecraft().thePlayer && ProxyClient.vrPlayerIds.containsKey(entity.getEntityId());
	}

	public static float getVRPlayerHeadHeight(EntityPlayer entity) {
		VRPlayerData data = ProxyClient.vrPlayerIds.get(entity.getEntityId());
		if (data != null && data.entityIds.size() > 0) {
			EntityVRObject entityVR = (EntityVRObject)entity.worldObj.getEntityByID(data.entityIds.get(0));
			if (entityVR != null) {
				return (float)entityVR.position.yCoord;
			}
		}
		return (float)entity.posY + 1.62F;
	}

	public static float getVRPlayerScale(EntityPlayer entity) {
		float height = getVRPlayerHeadHeight((EntityPlayer)entity) - 0.125F;
		return Math.max((height - (float)entity.posY) / 1.62F, 0.1F);
	}

	public static float getVRPlayerWorldScale(EntityPlayer entity) {
		VRPlayerData data = ProxyClient.vrPlayerIds.get(entity.getEntityId());
		if (data != null) {
			return data.worldScale;
		}
		return 1;
	}

	public static boolean getVRPlayerSeated(EntityPlayer entity) {
		VRPlayerData data = ProxyClient.vrPlayerIds.get(entity.getEntityId());
		if (data != null) {
			return data.seated;
		}
		return false;
	}
} public class ProxyServer extends ProxyCommon {
	public static Map<EntityPlayer, VRPlayerData> vrPlayers = new HashMap<EntityPlayer, VRPlayerData>();
	
	@Override
	public void registerNetworkChannels() {
		super.registerNetworkChannels();
		VivecraftForge.networkVersion = NetworkRegistry.INSTANCE.newSimpleChannel("MC|Vive|Version");
		//VivecraftForge.networkFreeMove = NetworkRegistry.INSTANCE.newSimpleChannel("MC|Vive|FreeMove"); // currently not used
		VivecraftForge.networkLegacy = NetworkRegistry.INSTANCE.newSimpleChannel("MC|Vive");
		VivecraftForge.networkOK = NetworkRegistry.INSTANCE.newSimpleChannel("MC|ViveOK");
		VivecraftForge.networkVersion.registerMessage(ViveMessage.Handle.class, ViveMessage.class, 86, Side.SERVER);
		VivecraftForge.networkLegacy.registerMessage(ViveMessage.Handle.class, ViveMessage.class, 112, Side.SERVER);
	}

	public static boolean isVRPlayer(EntityPlayer entity) {
		return ProxyServer.vrPlayers.containsKey(entity);
	}
	
	public static boolean getVRPlayerSeated(EntityPlayer entity) {
		VRPlayerData data = ProxyServer.vrPlayers.get(entity);
		if (data != null) {
			return data.seated;
		}
		return false;
	}
} @SideOnly(Side.CLIENT)
public enum Axis {
	PITCH(-1, 0, 0),
	YAW(0, -1, 0),
	ROLL(0, 0, -1),
	UNKNOWN(0, 0, 0);

	private Vector3 vector;

	private Axis(float x, float y, float z) {
		this.vector = new Vector3(x, y, z);
	}

	public Vector3 getVector() {
		return vector.copy();
	}
} @SideOnly(Side.CLIENT)
public class Quaternion {
	public float w;
	public float x;
	public float y;
	public float z;

	public Quaternion() {
		this.w = 1;
	}

	public Quaternion(float w, float x, float y, float z) {
		this.w = w;
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public Quaternion(Quaternion other) {
		this.w = other.w;
		this.x = other.x;
		this.y = other.y;
		this.z = other.z;
	}

	public Quaternion(Vector3 vector, float rotation) {
		rotation = (float)Math.toRadians(rotation);
		float sinRot = (float)Math.sin(rotation / 2);
		w = (float)Math.cos(rotation / 2);
		x = vector.x * sinRot;
		y = vector.y * sinRot;
		z = vector.z * sinRot;
	}

	public Quaternion(Axis axis, float rotation) {
		this(axis.getVector(), rotation);
	}

	public Quaternion(float pitch, float yaw, float roll, Angle.Order order) {
		// X Pitch Y Yaw Z Roll
		// YXZ Order
		Quaternion quatX = new Quaternion(new Vector3(1, 0, 0), pitch);
		Quaternion quatY = new Quaternion(new Vector3(0, 1, 0), yaw);
		Quaternion quatZ = new Quaternion(new Vector3(0, 0, 1), roll);
		Quaternion quat = null;
		switch (order) {
			case XYZ: quat = quatX.multiply(quatY).multiply(quatZ); break;
			case ZYX: quat = quatZ.multiply(quatY).multiply(quatX); break;
			case YXZ: quat = quatY.multiply(quatX).multiply(quatZ); break;
			case ZXY: quat = quatZ.multiply(quatX).multiply(quatY); break;
			case YZX: quat = quatY.multiply(quatZ).multiply(quatX); break;
			case XZY: quat = quatX.multiply(quatZ).multiply(quatY); break;
		}
		w = quat.w;
		x = quat.x;
		y = quat.y;
		z = quat.z;
	}

	public Quaternion(float pitch, float yaw, float roll) {
		this(pitch, yaw, roll, Angle.Order.YXZ);
	}

	public Quaternion(Angle angle) {
		this(angle.getPitch(), angle.getYaw(), angle.getRoll(), angle.getOrder());
	}

	public Quaternion(Matrix3f matrix) {
		this(matrix.m00, matrix.m01, matrix.m02, matrix.m10, matrix.m11, matrix.m12, matrix.m20, matrix.m21, matrix.m22);
	}

	public Quaternion(Matrix4f matrix) {
		this(matrix.m00, matrix.m01, matrix.m02, matrix.m10, matrix.m11, matrix.m12, matrix.m20, matrix.m21, matrix.m22);
	}

	private Quaternion(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) {
		float s;
		float tr = m00 + m11 + m22;
		if (tr >= 0.0) {
			s = (float)Math.sqrt(tr + 1.0);
			w = s * 0.5f;
			s = 0.5f / s;
			x = (m21 - m12) * s;
			y = (m02 - m20) * s;
			z = (m10 - m01) * s;
		} else {
			float max = Math.max(Math.max(m00, m11), m22);
			if (max == m00) {
				s = (float)Math.sqrt(m00 - (m11 + m22) + 1.0);
				x = s * 0.5f;
				s = 0.5f / s;
				y = (m01 + m10) * s;
				z = (m20 + m02) * s;
				w = (m21 - m12) * s;
			} else if (max == m11) {
				s = (float)Math.sqrt(m11 - (m22 + m00) + 1.0);
				y = s * 0.5f;
				s = 0.5f / s;
				z = (m12 + m21) * s;
				x = (m01 + m10) * s;
				w = (m02 - m20) * s;
			} else {
				s = (float)Math.sqrt(m22 - (m00 + m11) + 1.0);
				z = s * 0.5f;
				s = 0.5f / s;
				x = (m20 + m02) * s;
				y = (m12 + m21) * s;
				w = (m10 - m01) * s;
			}
		}
	}

	public Quaternion copy() {
		return new Quaternion(this);
	}

	public float getW() {
		return w;
	}

	public void setW(float w) {
		this.w = w;
	}

	public float getX() {
		return x;
	}

	public void setX(float x) {
		this.x = x;
	}

	public float getY() {
		return y;
	}

	public void setY(float y) {
		this.y = y;
	}

	public float getZ() {
		return z;
	}

	public void setZ(float z) {
		this.z = z;
	}

	public void normalize() {
		float norm = (float)Math.sqrt(w * w + x * x + y * y + z * z);
		if (norm > 0) {
			w /= norm;
			x /= norm;
			y /= norm;
			z /= norm;
		} else {
			w = 1;
			x = 0;
			y = 0;
			z = 0;
		}
	}

	public Quaternion normalized() {
		float newW, newX, newY, newZ;
		float norm = (float)Math.sqrt(w * w + x * x + y * y + z * z);
		if (norm > 0) {
			newW = w / norm;
			newX = x / norm;
			newY = y / norm;
			newZ = z / norm;
		} else {
			newW = 1;
			newX = 0;
			newY = 0;
			newZ = 0;
		}
		return new Quaternion(newW, newX, newY, newZ);
	}

	public Quaternion rotate(Axis axis, float degrees) {
		//return this.multiply(new Quaternion(axis.getVector(), degrees));
		Matrix4f matrix = getMatrix();
		matrix.rotate((float)Math.toRadians(degrees), Util.convertVector(axis.getVector()));
		return new Quaternion(matrix);
	}

	public Quaternion multiply(Quaternion other) {
		float newW = w * other.w - x * other.x - y * other.y - z * other.z;
		float newX = w * other.x + other.w * x + y * other.z - z * other.y;
		float newY = w * other.y + other.w * y - x * other.z + z * other.x;
		float newZ = w * other.z + other.w * z + x * other.y - y * other.x;
		return new Quaternion(newW, newX, newY, newZ);
	}

	public Vector3 multiply(Vector3 vec) {
		float num = this.x * 2f;
		float num2 = this.y * 2f;
		float num3 = this.z * 2f;
		float num4 = this.x * num;
		float num5 = this.y * num2;
		float num6 = this.z * num3;
		float num7 = this.x * num2;
		float num8 = this.x * num3;
		float num9 = this.y * num3;
		float num10 = this.w * num;
		float num11 = this.w * num2;
		float num12 = this.w * num3;
		Vector3 result = new Vector3();
		result.x = (1f - (num5 + num6)) * vec.x + (num7 - num12) * vec.y + (num8 + num11) * vec.z;
		result.y = (num7 + num12) * vec.x + (1f - (num4 + num6)) * vec.y + (num9 - num10) * vec.z;
		result.z = (num8 - num11) * vec.x + (num9 + num10) * vec.y + (1f - (num4 + num5)) * vec.z;
		return result;
	}

	public Matrix4f getMatrix() {
		Matrix4f matrix = new Matrix4f();
		float sqw = w * w;
		float sqx = x * x;
		float sqy = y * y;
		float sqz = z * z;

		// invs (inverse square length) is only required if quaternion is not already normalised
		float invs = 1 / (sqx + sqy + sqz + sqw);
		matrix.m00 = (sqx - sqy - sqz + sqw) * invs; // since sqw + sqx + sqy + sqz =1/invs*invs
		matrix.m11 = (-sqx + sqy - sqz + sqw) * invs;
		matrix.m22 = (-sqx - sqy + sqz + sqw) * invs;

		float tmp1 = x * y;
		float tmp2 = z * w;
		matrix.m10 = 2 * (tmp1 + tmp2) * invs;
		matrix.m01 = 2 * (tmp1 - tmp2) * invs;

		tmp1 = x * z;
		tmp2 = y * w;
		matrix.m20 = 2 * (tmp1 - tmp2) * invs;
		matrix.m02 = 2 * (tmp1 + tmp2) * invs;

		tmp1 = y * z;
		tmp2 = x * w;
		matrix.m21 = 2 * (tmp1 + tmp2) * invs;
		matrix.m12 = 2 * (tmp1 - tmp2) * invs;

		return matrix;
	}

	@Override
	public int hashCode() {
		int hash = 3;
		hash = 23 * hash + Float.floatToIntBits(this.w);
		hash = 23 * hash + Float.floatToIntBits(this.x);
		hash = 23 * hash + Float.floatToIntBits(this.y);
		hash = 23 * hash + Float.floatToIntBits(this.z);
		return hash;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final Quaternion other = (Quaternion)obj;
		if (Float.floatToIntBits(this.w) != Float.floatToIntBits(other.w)) {
			return false;
		}
		if (Float.floatToIntBits(this.x) != Float.floatToIntBits(other.x)) {
			return false;
		}
		if (Float.floatToIntBits(this.y) != Float.floatToIntBits(other.y)) {
			return false;
		}
		if (Float.floatToIntBits(this.z) != Float.floatToIntBits(other.z)) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return "Quaternion{" + "w=" + w + ", x=" + x + ", y=" + y + ", z=" + z + '}';
	}
} public class Util {
	private Util() {
	}

	@SideOnly(Side.CLIENT)
	public static Vector2f convertVector(Vector2 vector) {
		return new Vector2f(vector.getX(), vector.getY());
	}

	@SideOnly(Side.CLIENT)
	public static Vector2 convertVector(Vector2f vector) {
		return new Vector2(vector.getX(), vector.getY());
	}

	@SideOnly(Side.CLIENT)
	public static Vector3f convertVector(Vector3 vector) {
		return new Vector3f(vector.getX(), vector.getY(), vector.getZ());
	}

	@SideOnly(Side.CLIENT)
	public static Vector3 convertVector(Vector3f vector) {
		return new Vector3(vector.getX(), vector.getY(), vector.getZ());
	}

	@SideOnly(Side.CLIENT)
	public static Vector3 convertVector(Vec3 vector) {
		return new Vector3((float)vector.xCoord, (float)vector.yCoord, (float)vector.zCoord);
	}

	@SideOnly(Side.CLIENT)
	public static Quaternion quatLerp(Quaternion start, Quaternion end, float fraction) {
		Quaternion quat = new Quaternion();
		quat.w = start.w + (end.w - start.w) * fraction;
		quat.x = start.x + (end.x - start.x) * fraction;
		quat.y = start.y + (end.y - start.y) * fraction;
		quat.z = start.z + (end.z - start.z) * fraction;
		return quat;
	}

	@SideOnly(Side.CLIENT)
	public static Matrix4f matrix3to4(Matrix3f matrix) {
		Matrix4f mat = new Matrix4f();
		mat.m00 = matrix.m00;
		mat.m01 = matrix.m01;
		mat.m02 = matrix.m02;
		mat.m10 = matrix.m10;
		mat.m11 = matrix.m11;
		mat.m12 = matrix.m12;
		mat.m20 = matrix.m20;
		mat.m21 = matrix.m21;
		mat.m22 = matrix.m22;
		return mat;
	}
} public class VRPlayerData {
	/**
	 * Only on server
	 */
	public List<EntityVRObject> entities = new ArrayList<EntityVRObject>(3);
	public List<Integer> entityIds = new ArrayList<Integer>(3);
	public boolean newAPI;
	public boolean reverseHands;
	public float worldScale = 1;
	public boolean seated;

	public VRPlayerData copy() {
		VRPlayerData data = new VRPlayerData();
		data.newAPI = newAPI;
		data.reverseHands = reverseHands;
		data.worldScale = worldScale;
		data.seated = seated;
		data.entities = new ArrayList<EntityVRObject>(entities);
		data.entityIds = new ArrayList<Integer>(entityIds);
		return data;
	}
} @SideOnly(Side.CLIENT)
public class Vector3 {
	protected float x;
	protected float y;
	protected float z;

	public Vector3() {
	}

	public Vector3(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public Vector3(Vector3 other) {
		this.x = other.x;
		this.y = other.y;
		this.z = other.z;
	}

	public Vector3(org.lwjgl.util.vector.Vector3f other) {
		this.x = other.x;
		this.y = other.y;
		this.z = other.z;
	}

	public Vector3 copy() {
		return new Vector3(this);
	}

	public float getX() {
		return x;
	}

	public void setX(float x) {
		this.x = x;
	}

	public float getY() {
		return y;
	}

	public void setY(float y) {
		this.y = y;
	}

	public float getZ() {
		return z;
	}

	public void setZ(float z) {
		this.z = z;
	}

	public void set(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public void set(Vector3 other) {
		x = other.x;
		y = other.y;
		z = other.z;
	}

	public Vector3 add(Vector3 other) {
		return new Vector3(this.x + other.x, this.y + other.y, this.z + other.z);
	}

	public Vector3 add(float number) {
		return new Vector3(this.x + number, this.y + number, this.z + number);
	}

	public Vector3 subtract(Vector3 other) {
		return new Vector3(this.x - other.x, this.y - other.y, this.z - other.z);
	}

	public Vector3 subtract(float number) {
		return new Vector3(this.x - number, this.y - number, this.z - number);
	}

	public Vector3 multiply(float number) {
		return new Vector3(this.x * number, this.y * number, this.z * number);
	}

	public Vector3 multiply(Matrix3f matrix) {
		float newX = matrix.m00 * x + matrix.m01 * y + matrix.m02 * z;
		float newY = matrix.m10 * x + matrix.m11 * y + matrix.m12 * z;
		float newZ = matrix.m20 * x + matrix.m21 * y + matrix.m22 * z;
		return new Vector3(newX, newY, newZ);
	}

	public Vector3 multiply(Matrix4f matrix) {
		float newX = matrix.m00 * x + matrix.m01 * y + matrix.m02 * z + matrix.m03;
		float newY = matrix.m10 * x + matrix.m11 * y + matrix.m12 * z + matrix.m13;
		float newZ = matrix.m20 * x + matrix.m21 * y + matrix.m22 * z + matrix.m23;
		return new Vector3(newX, newY, newZ);
	}

	public Vector3 divide(float number) {
		return new Vector3(this.x / number, this.y / number, this.z / number);
	}

	public Vector3 negate() {
		return new Vector3(-this.x, -this.y, -this.z);
	}

	public Angle angle(Vector3 other) {
		float deltaX = other.x - x;
		float deltaZ = other.z - z;
		float pitch = (float)Math.toDegrees(Math.atan2(other.y - y, Math.sqrt(deltaX * deltaX + deltaZ * deltaZ)));
		float yaw = (float)Math.toDegrees(Math.atan2(-deltaX, -deltaZ));
		/*float pitch = (float)Math.toDegrees(Math.asin(other.y - y));
        float yaw = (float)Math.toDegrees(Math.atan2(-(other.x - x), -(other.z - z)));*/
        return new Angle(pitch, yaw);
	}

	public float length() {
		return (float)Math.sqrt(x * x + y * y + z * z);
	}

	public float lengthSquared() {
		return x * x + y * y + z * z;
	}

	public float distance(Vector3 other) {
		float xx = other.x - x;
		float yy = other.y - y;
		float zz = other.z - z;
		return (float)Math.sqrt(xx * xx + yy * yy + zz * zz);
	}

	public float distanceSquared(Vector3 other) {
		float xx = other.x - x;
		float yy = other.y - y;
		float zz = other.z - z;
		return xx * xx + yy * yy + zz * zz;
	}

	public float distance2D(Vector2 other) {
		return other.subtract(new Vector2(this.x, this.z)).length();
	}

	public float distanceSquared2D(Vector2 other) {
		return other.subtract(new Vector2(this.x, this.z)).lengthSquared();
	}

	public float distance2D(Vector3 other) {
		return this.distance2D(new Vector2(other.x, other.z));
	}

	public float distanceSquared2D(Vector3 other) {
		return this.distanceSquared2D(new Vector2(other.x, other.z));
	}

	public void normalize() {
		set(divide(length()));
	}

	public Vector3 normalized() {
		return divide(length());
	}

	public float dot(Vector3 other) {
		return x * other.x + y * other.y + z * other.z;
	}

	public Vector3 cross(Vector3 other) {
		return new Vector3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x);
	}

	public Vector3 project(Vector3 other) {
		return other.multiply(other.dot(this) / other.dot(other));
	}

	public static Vector3 forward() {
		return new Vector3(0, 0, -1);
	}

	public static Vector3 up() {
		return new Vector3(0, -1, 0);
	}

	public static Vector3 right() {
		return new Vector3(-1, 0, 0);
	}

	public static Vector3 lerp(Vector3 start, Vector3 end, float fraction) {
		return start.add(end.subtract(start).multiply(fraction));
	}

	public static Vector3 slerp(Vector3 start, Vector3 end, float fraction) {
		float dot = start.dot(end);
		float theta = (float)Math.acos(dot) * fraction;
		Vector3 relative = end.subtract(start.multiply(dot));
		relative.normalize();
		return start.multiply((float)Math.cos(theta)).add(relative.multiply((float)Math.sin(theta)));
	}

	public static Matrix3f lookMatrix(Vector3 forward, Vector3 up) {
		Vector3 vector = forward.normalized();
		Vector3 vector2 = up.cross(vector).normalized();
		Vector3 vector3 = vector.cross(vector2);
		Matrix3f matrix = new Matrix3f();
		matrix.m00 = vector2.x;
		matrix.m01 = vector2.y;
		matrix.m02 = vector2.z;
		matrix.m10 = vector3.x;
		matrix.m11 = vector3.y;
		matrix.m12 = vector3.z;
		matrix.m20 = vector.x;
		matrix.m21 = vector.y;
		matrix.m22 = vector.z;
		return matrix;
	}

	@Override
	public int hashCode() {
		int hash = 7;
		hash = 53 * hash + Float.floatToIntBits(this.x);
		hash = 53 * hash + Float.floatToIntBits(this.y);
		hash = 53 * hash + Float.floatToIntBits(this.z);
		return hash;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final Vector3 other = (Vector3)obj;
		if (Float.floatToIntBits(this.x) != Float.floatToIntBits(other.x)) {
			return false;
		}
		if (Float.floatToIntBits(this.y) != Float.floatToIntBits(other.y)) {
			return false;
		}
		if (Float.floatToIntBits(this.z) != Float.floatToIntBits(other.z)) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return "Vector3{" + "x=" + x + ", y=" + y + ", z=" + z + '}';
	}
}
import java.nio.FloatBuffer;
import java.util.UUID;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.util.vector.Matrix3f;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import com.mojang.authlib.GameProfile;
import com.techjar.vivecraftforge.client.render.model.ModelVRHead;
import com.techjar.vivecraftforge.entity.EntityVRArm;
import com.techjar.vivecraftforge.entity.EntityVRHead;
import com.techjar.vivecraftforge.entity.EntityVROffHandArm;
import com.techjar.vivecraftforge.entity.EntityVRObject;
import com.techjar.vivecraftforge.entity.EntityVRMainArm;
import com.techjar.vivecraftforge.proxy.ProxyClient;
import com.techjar.vivecraftforge.proxy.ProxyServer;
import com.techjar.vivecraftforge.util.Axis;
import com.techjar.vivecraftforge.util.Quaternion;
import com.techjar.vivecraftforge.util.Util;
import com.techjar.vivecraftforge.util.VRPlayerData;
import com.techjar.vivecraftforge.util.Vector3;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderBiped;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.tileentity.TileEntitySkullRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringUtils;
import net.minecraftforge.client.ForgeHooksClient;
package com.techjar.vivecraftforge.client.render.entity;





public abstract class RenderEntityVRObject extends Render {
    private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png");
	protected ModelBase model;
	protected ModelBiped modelArmor;
    protected ModelBiped modelArmorChestplate;
	protected int armorSlot;

	public RenderEntityVRObject() {
		modelArmorChestplate = new ModelBiped(1.0F);
		modelArmor = new ModelBiped(0.5F);
	}

	/**
	 * lol coords are not used here so don't even try
	 */
	@Override
	public void doRender(Entity entity, double x, double y, double z, float yaw, float pitch) {
		EntityVRObject entityVR = (EntityVRObject)entity;
		if (entityVR.getEntityPlayer() == null || entityVR.getEntityPlayer() == Minecraft.getMinecraft().thePlayer) return;
		VRPlayerData data = ProxyClient.vrPlayerIds.get(entityVR.getEntityPlayer().getEntityId());