package mcjty.deepresonance.varia; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; public class QuadTree { private DiscreteAABB box; private QuadTree child1; private QuadTree child2; private float blocker = 1.0f; // 0.0 is blocked, 1.0 is transparent public QuadTree(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { box = DiscreteAABB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ); } public void addBlocker(int x, int y, int z, float blocker) { addBlocker(new BlockPos(x, y, z), blocker); } // Return -1 if blockers inside this are different. Otherwise blocker value. public float addBlocker(BlockPos coordinate, float blocker) { if (child1 != null) { if (child1.box.isVecInside(coordinate)) { float b = child1.addBlocker(coordinate, blocker); if (child2.blocker >= 0.0 && Math.abs(b - child2.blocker) < 0.01) { // Blockers are almost the same. Optimize this.blocker = b; this.child1 = null; this.child2 = null; return b; } this.blocker = -1; return -1; } else if (child2.box.isVecInside(coordinate)) { float b = child2.addBlocker(coordinate, blocker); if (child1.blocker >= 0.0 && Math.abs(b - child1.blocker) < 0.01) { // Blockers are almost the same. Optimize this.blocker = b; this.child1 = null; this.child2 = null; return b; } this.blocker = -1; return -1; } else { System.out.println("Impossible! Point " + coordinate + " is not in either box!"); System.out.println(" child1.box = " + child1.box); System.out.println(" child2.box = " + child2.box); this.blocker = -1; return -1; } } else { int lx = box.maxX - box.minX; int ly = box.maxY - box.minY; int lz = box.maxZ - box.minZ; int largest; int axis; if (lx >= ly && lx >= lz) { largest = lx; axis = 0; } else if (ly >= lz) { largest = ly; axis = 1; } else { largest = lz; axis = 2; } if (largest > 1) { switch (axis) { case 0: { int middle = (box.maxX + box.minX) / 2; child1 = new QuadTree(box.minX, box.minY, box.minZ, middle, box.maxY, box.maxZ); child2 = new QuadTree(middle, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ); break; } case 1: { int middle = (box.maxY + box.minY) / 2; child1 = new QuadTree(box.minX, box.minY, box.minZ, box.maxX, middle, box.maxZ); child2 = new QuadTree(box.minX, middle, box.minZ, box.maxX, box.maxY, box.maxZ); break; } case 2: { int middle = (box.maxZ + box.minZ) / 2; child1 = new QuadTree(box.minX, box.minY, box.minZ, box.maxX, box.maxY, middle); child2 = new QuadTree(box.minX, box.minY, middle, box.maxX, box.maxY, box.maxZ); break; } } child1.blocker = this.blocker; child2.blocker = this.blocker; this.blocker = -1; return addBlocker(coordinate, blocker); } else { // Don't split. this.blocker = blocker; return blocker; } } } public double factor(int x1, int y1, int z1, int x2, int y2, int z2) { Vec3d p1 = new Vec3d(x1 + .5, y1 + .5, z1 + .5); Vec3d p2 = new Vec3d(x2 + .5, y2 + .5, z2 + .5); return factor(new Ray(p1, p2)); } // Calculate radiation for a 2-high character public double factor2(int x1, int y1, int z1, int x2, int y2, int z2) { Vec3d p1 = new Vec3d(x1 + .5, y1 + .5, z1 + .5); Vec3d p2 = new Vec3d(x2 + .5, y2 + .5, z2 + .5); double f1 = factor(new Ray(p1, p2)); Vec3d p3 = new Vec3d(x2 + .5, y2 + 1.1, z2 + .5); double f2 = factor(new Ray(p1, p3)); return Math.max(f1, f2); } private double factor(Ray ray) { if (child1 != null) { double factor = 1.0; if (testIntersect(child1.box, ray)) { factor *= child1.factor(ray); } if (testIntersect(child2.box, ray)) { factor *= child2.factor(ray); } return factor; } else { return blocker; } } private static boolean testIntersect(DiscreteAABB box, Ray ray) { Vec3d invDir = ray.getInvDir(); boolean signDirX = invDir.xCoord < 0; boolean signDirY = invDir.yCoord < 0; boolean signDirZ = invDir.zCoord < 0; double v = signDirX ? box.maxX : box.minX; double tmin = (v - ray.getStart().xCoord) * invDir.xCoord; v = signDirX ? box.minX : box.maxX; double tmax = (v - ray.getStart().xCoord) * invDir.xCoord; v = signDirY ? box.maxY : box.minY; double tymin = (v - ray.getStart().yCoord) * invDir.yCoord; v = signDirY ? box.minY : box.maxY; double tymax = (v - ray.getStart().yCoord) * invDir.yCoord; if ((tmin > tymax) || (tymin > tmax)) { return false; } if (tymin > tmin) { tmin = tymin; } if (tymax < tmax) { tmax = tymax; } v = signDirZ ? box.maxZ : box.minZ; double tzmin = (v - ray.getStart().zCoord) * invDir.zCoord; v = signDirZ ? box.minZ : box.maxZ; double tzmax = (v - ray.getStart().zCoord) * invDir.zCoord; if ((tmin > tzmax) || (tzmin > tmax)) { return false; } if (tzmin > tmin) { tmin = tzmin; } if (tzmax < tmax) { tmax = tzmax; } if ((tmin < ray.getLength()) && (tmax > 0.01)) { return true; } return false; } private void dump(int indent) { if (child1 == null) { System.out.println(" ".substring(0, indent) + "Leaf: " + box + ", blocker=" + blocker); } else { System.out.println(" ".substring(0, indent) + "Node: " + box); child1.dump(indent + 2); child2.dump(indent + 2); } } private int treeSize() { if (child1 == null) { return 1; } else { return 1 + child1.treeSize() + child2.treeSize(); } } private static class Ray { private Vec3d start; private Vec3d dir; private Vec3d invDir; private double length; public Ray(Vec3d start, Vec3d end) { this.start = start; this.dir = end.subtract(start); length = this.dir.lengthVector(); this.dir = this.dir.normalize(); this.invDir = new Vec3d(1.0 / this.dir.xCoord, 1.0 / this.dir.yCoord, 1.0 / this.dir.zCoord); } public Vec3d getDir() { return dir; } public Vec3d getInvDir() { return invDir; } public Vec3d getStart() { return start; } public double getLength() { return length; } } public static void main(String[] args) { int dim = 100; QuadTree tree = new QuadTree(0, 0, 0, dim, dim, dim); for (int y = 0; y <= 5; y++) { for (int z = 0; z <= dim; z++) { tree.addBlocker(3, y, z, 0.5f); tree.addBlocker(20, y, z, 0.5f); tree.addBlocker(21, y, z, 0.5f); } } System.out.println("Twice Blocked: " + tree.factor(1, 3, 3, 40, 3, 3)); System.out.println("Once Blocked: " + tree.factor(1, 3, 3, 10, 3, 3)); System.out.println("Not Blocked: " + tree.factor(1, 7, 3, 8, 7, 3)); System.out.println("tree.treeSize() = " + tree.treeSize()); for (int y = 0; y <= 5; y++) { for (int z = 0; z <= dim; z++) { tree.addBlocker(3, y, z, 0.5f); tree.addBlocker(20, y, z, 1.0f); tree.addBlocker(21, y, z, 0.5f); } } System.out.println("Twice Blocked: " + tree.factor(1, 3, 3, 40, 3, 3)); System.out.println("Once Blocked: " + tree.factor(1, 3, 3, 10, 3, 3)); System.out.println("Not Blocked: " + tree.factor(1, 7, 3, 8, 7, 3)); System.out.println("tree.treeSize() = " + tree.treeSize()); // tree.dump(0); } }