package codechicken.lib.raytracer; import codechicken.lib.math.MathHelper; import codechicken.lib.vec.BlockCoord; import codechicken.lib.vec.Cuboid6; import codechicken.lib.vec.Vector3; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.BlockPos; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.List; public class RayTracer { private Vector3 vec = new Vector3(); private Vector3 vec2 = new Vector3(); private Vector3 s_vec = new Vector3(); private double s_dist; private int s_side; private IndexedCuboid6 c_cuboid; private static ThreadLocal<RayTracer> t_inst = new ThreadLocal<RayTracer>(); public static RayTracer instance() { RayTracer inst = t_inst.get(); if (inst == null) { t_inst.set(inst = new RayTracer()); } return inst; } private void traceSide(int side, Vector3 start, Vector3 end, Cuboid6 cuboid) { vec.set(start); Vector3 hit = null; switch (side) { case 0: hit = vec.XZintercept(end, cuboid.min.y); break; case 1: hit = vec.XZintercept(end, cuboid.max.y); break; case 2: hit = vec.XYintercept(end, cuboid.min.z); break; case 3: hit = vec.XYintercept(end, cuboid.max.z); break; case 4: hit = vec.YZintercept(end, cuboid.min.x); break; case 5: hit = vec.YZintercept(end, cuboid.max.x); break; } if (hit == null) { return; } switch (side) { case 0: case 1: if (!MathHelper.between(cuboid.min.x, hit.x, cuboid.max.x) || !MathHelper.between(cuboid.min.z, hit.z, cuboid.max.z)) { return; } break; case 2: case 3: if (!MathHelper.between(cuboid.min.x, hit.x, cuboid.max.x) || !MathHelper.between(cuboid.min.y, hit.y, cuboid.max.y)) { return; } break; case 4: case 5: if (!MathHelper.between(cuboid.min.y, hit.y, cuboid.max.y) || !MathHelper.between(cuboid.min.z, hit.z, cuboid.max.z)) { return; } break; } double dist = vec2.set(hit).subtract(start).magSquared(); if (dist < s_dist) { s_side = side; s_dist = dist; s_vec.set(vec); } } private boolean rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid) { s_dist = Double.MAX_VALUE; s_side = -1; for (int i = 0; i < 6; i++) { traceSide(i, start, end, cuboid); } return s_side >= 0; } public ExtendedMOP rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid, BlockCoord pos, Object data) { return rayTraceCuboid(start, end, cuboid) ? new ExtendedMOP(s_vec, s_side, pos, data, s_dist) : null; } public ExtendedMOP rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid, Entity entity, Object data) { return rayTraceCuboid(start, end, cuboid) ? new ExtendedMOP(entity, s_vec, data, s_dist) : null; } public void rayTraceCuboids(Vector3 start, Vector3 end, List<IndexedCuboid6> cuboids, BlockCoord pos, Block block, List<ExtendedMOP> hitList) { for (IndexedCuboid6 cuboid : cuboids) { ExtendedMOP mop = rayTraceCuboid(start, end, cuboid, pos, cuboid.data); if (mop != null) { hitList.add(mop); } } } public static MovingObjectPosition retraceBlock(World world, EntityPlayer player, BlockPos pos) { IBlockState b = world.getBlockState(pos); Vec3 headVec = getCorrectedHeadVec(player); Vec3 lookVec = player.getLook(1.0F); double reach = getBlockReachDistance(player); Vec3 endVec = headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach); return b.getBlock().collisionRayTrace(world, pos, headVec, endVec); } private static double getBlockReachDistance_server(EntityPlayerMP player) { return player.theItemInWorldManager.getBlockReachDistance(); } @SideOnly(Side.CLIENT) private static double getBlockReachDistance_client() { return Minecraft.getMinecraft().playerController.getBlockReachDistance(); } public static MovingObjectPosition retrace(EntityPlayer player) { return retrace(player, getBlockReachDistance(player)); } public static MovingObjectPosition retrace(EntityPlayer player, double reach) { Vec3 headVec = getCorrectedHeadVec(player); Vec3 lookVec = player.getLook(1); Vec3 endVec = headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach); return player.worldObj.rayTraceBlocks(headVec, endVec, true, false, true); } public static Vec3 getCorrectedHeadVec(EntityPlayer player) { Vector3 v = Vector3.fromEntity(player); if (player.worldObj.isRemote) { v.y += player.getEyeHeight() - player.getDefaultEyeHeight();//compatibility with eye height changing mods } else { v.y += player.getEyeHeight(); if (player instanceof EntityPlayerMP && player.isSneaking()) { v.y -= 0.08; } } return v.vec3(); } public static Vec3 getStartVec(EntityPlayer player) { return getCorrectedHeadVec(player); } public static double getBlockReachDistance(EntityPlayer player) { return player.worldObj.isRemote ? getBlockReachDistance_client() : player instanceof EntityPlayerMP ? getBlockReachDistance_server((EntityPlayerMP) player) : 5D; } public static Vec3 getEndVec(EntityPlayer player) { Vec3 headVec = getCorrectedHeadVec(player); Vec3 lookVec = player.getLook(1.0F); double reach = getBlockReachDistance(player); return headVec.addVector(lookVec.xCoord * reach, lookVec.yCoord * reach, lookVec.zCoord * reach); } }