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 cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.block.Block; 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.MovingObjectPosition; import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.util.Vec3; import net.minecraft.world.World; 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 t_inst = new ThreadLocal(); 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); } } public MovingObjectPosition 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); if(s_side < 0) return null; MovingObjectPosition mop = new MovingObjectPosition(0, 0, 0, s_side, s_vec.toVec3D()); mop.typeOfHit = null; return mop; } public MovingObjectPosition rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid, BlockCoord pos) { MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid); if(mop != null) { mop.typeOfHit = MovingObjectType.BLOCK; mop.blockX = pos.x; mop.blockY = pos.y; mop.blockZ = pos.z; } return mop; } public MovingObjectPosition rayTraceCuboid(Vector3 start, Vector3 end, Cuboid6 cuboid, Entity e) { MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid); if(mop != null) { mop.typeOfHit = MovingObjectType.ENTITY; mop.entityHit = e; } return mop; } public MovingObjectPosition rayTraceCuboids(Vector3 start, Vector3 end, List cuboids) { double c_dist = Double.MAX_VALUE; MovingObjectPosition c_hit = null; for(IndexedCuboid6 cuboid : cuboids) { MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid); if(mop != null && s_dist < c_dist) { mop = new ExtendedMOP(mop, cuboid.data, s_dist); c_dist = s_dist; c_hit = mop; c_cuboid = cuboid; } } return c_hit; } public MovingObjectPosition rayTraceCuboids(Vector3 start, Vector3 end, List cuboids, BlockCoord pos, Block block) { MovingObjectPosition mop = rayTraceCuboids(start, end, cuboids); if(mop != null) { mop.typeOfHit = MovingObjectType.BLOCK; mop.blockX = pos.x; mop.blockY = pos.y; mop.blockZ = pos.z; if(block != null) c_cuboid.add(new Vector3(-pos.x, -pos.y, -pos.z)).setBlockBounds(block); } return mop; } public void rayTraceCuboids(Vector3 start, Vector3 end, List cuboids, BlockCoord pos, Block block, List hitList) { for(IndexedCuboid6 cuboid : cuboids) { MovingObjectPosition mop = rayTraceCuboid(start, end, cuboid); if(mop != null) { ExtendedMOP emop = new ExtendedMOP(mop, cuboid.data, s_dist); emop.typeOfHit = MovingObjectType.BLOCK; emop.blockX = pos.x; emop.blockY = pos.y; emop.blockZ = pos.z; hitList.add(emop); } } } public static MovingObjectPosition retraceBlock(World world, EntityPlayer player, int x, int y, int z) { Block block = world.getBlock(x, y, z); 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 block.collisionRayTrace(world, x, y, z, 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(World world, EntityPlayer player) { return reTrace(world, player, getBlockReachDistance(player)); } public static MovingObjectPosition reTrace(World world, 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 world.func_147447_a(headVec, endVec, true, false, true); } public static Vec3 getCorrectedHeadVec(EntityPlayer player) { Vec3 v = Vec3.createVectorHelper(player.posX, player.posY, player.posZ); if(player.worldObj.isRemote) { v.yCoord+=player.getEyeHeight()-player.getDefaultEyeHeight();//compatibility with eye height changing mods } else { v.yCoord+=player.getEyeHeight(); if(player instanceof EntityPlayerMP && player.isSneaking()) v.yCoord-=0.08; } return v; } 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); } }