--- a/net/minecraft/world/phys/shapes/Shapes.java
+++ b/net/minecraft/world/phys/shapes/Shapes.java
@@ -19,16 +19,17 @@
         DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(1, 1, 1);
         discreteVoxelShape.fill(0, 0, 0);
         return new CubeVoxelShape(discreteVoxelShape);
-    });
+    }); public static VoxelShape getFullUnoptimisedCube() { return BLOCK; } // Paper - OBFHELPER
     public static final VoxelShape INFINITY = box(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
     private static final VoxelShape EMPTY = new ArrayVoxelShape(new BitSetDiscreteVoxelShape(0, 0, 0), (DoubleList)(new DoubleArrayList(new double[]{0.0D})), (DoubleList)(new DoubleArrayList(new double[]{0.0D})), (DoubleList)(new DoubleArrayList(new double[]{0.0D})));
+    public static final io.papermc.paper.voxel.AABBVoxelShape BLOCK_OPTIMISED = new io.papermc.paper.voxel.AABBVoxelShape(new AABB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)); // Paper
 
     public static VoxelShape empty() {
         return EMPTY;
     }
 
     public static VoxelShape block() {
-        return BLOCK;
+        return BLOCK_OPTIMISED; // Paper
     }
 
     public static VoxelShape box(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
@@ -41,29 +42,14 @@
 
     public static VoxelShape create(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
         if (!(maxX - minX < 1.0E-7D) && !(maxY - minY < 1.0E-7D) && !(maxZ - minZ < 1.0E-7D)) {
-            int i = findBits(minX, maxX);
-            int j = findBits(minY, maxY);
-            int k = findBits(minZ, maxZ);
-            if (i >= 0 && j >= 0 && k >= 0) {
-                if (i == 0 && j == 0 && k == 0) {
-                    return block();
-                } else {
-                    int l = 1 << i;
-                    int m = 1 << j;
-                    int n = 1 << k;
-                    BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = BitSetDiscreteVoxelShape.withFilledBounds(l, m, n, (int)Math.round(minX * (double)l), (int)Math.round(minY * (double)m), (int)Math.round(minZ * (double)n), (int)Math.round(maxX * (double)l), (int)Math.round(maxY * (double)m), (int)Math.round(maxZ * (double)n));
-                    return new CubeVoxelShape(bitSetDiscreteVoxelShape);
-                }
-            } else {
-                return new ArrayVoxelShape(BLOCK.shape, (DoubleList)DoubleArrayList.wrap(new double[]{minX, maxX}), (DoubleList)DoubleArrayList.wrap(new double[]{minY, maxY}), (DoubleList)DoubleArrayList.wrap(new double[]{minZ, maxZ}));
-            }
+            return new io.papermc.paper.voxel.AABBVoxelShape(new AABB(minX, minY, minZ, maxX, maxY, maxZ)); // Paper
         } else {
             return empty();
         }
     }
 
     public static VoxelShape create(AABB box) {
-        return create(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ);
+        return new io.papermc.paper.voxel.AABBVoxelShape(box); // Paper
     }
 
     @VisibleForTesting
@@ -125,6 +111,20 @@
     }
 
     public static boolean joinIsNotEmpty(VoxelShape shape1, VoxelShape shape2, BooleanOp predicate) {
+        // Paper start - optimise voxelshape
+        if (predicate == BooleanOp.AND) {
+            if (shape1 instanceof io.papermc.paper.voxel.AABBVoxelShape && shape2 instanceof io.papermc.paper.voxel.AABBVoxelShape) {
+                return io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(((io.papermc.paper.voxel.AABBVoxelShape)shape1).aabb, ((io.papermc.paper.voxel.AABBVoxelShape)shape2).aabb);
+            } else if (shape1 instanceof io.papermc.paper.voxel.AABBVoxelShape && shape2 instanceof ArrayVoxelShape) {
+                return ((ArrayVoxelShape)shape2).intersects(((io.papermc.paper.voxel.AABBVoxelShape)shape1).aabb);
+            } else if (shape2 instanceof io.papermc.paper.voxel.AABBVoxelShape && shape1 instanceof ArrayVoxelShape) {
+                return ((ArrayVoxelShape)shape1).intersects(((io.papermc.paper.voxel.AABBVoxelShape)shape2).aabb);
+            }
+        }
+        return joinIsNotEmptyVanilla(shape1, shape2, predicate);
+    }
+    public static boolean joinIsNotEmptyVanilla(VoxelShape shape1, VoxelShape shape2, BooleanOp predicate) {
+        // Paper end - optimise voxelshape
         if (predicate.apply(false, false)) {
             throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException());
         } else {
@@ -196,6 +196,43 @@
     }
 
     public static VoxelShape getFaceShape(VoxelShape shape, Direction direction) {
+        // Paper start - optimise shape creation here for lighting, as this shape is going to be used
+        // for transparency checks
+        if (shape == BLOCK || shape == BLOCK_OPTIMISED) {
+            return BLOCK_OPTIMISED;
+        } else if (shape == empty()) {
+            return empty();
+        }
+
+        if (shape instanceof io.papermc.paper.voxel.AABBVoxelShape) {
+            final AABB box = ((io.papermc.paper.voxel.AABBVoxelShape)shape).aabb;
+            switch (direction) {
+                case WEST: // -X
+                case EAST: { // +X
+                    final boolean useEmpty = direction == Direction.EAST ? !DoubleMath.fuzzyEquals(box.maxX, 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) :
+                            !DoubleMath.fuzzyEquals(box.minX, 0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON);
+                    return useEmpty ? empty() : new io.papermc.paper.voxel.AABBVoxelShape(new AABB(0.0, box.minY, box.minZ, 1.0, box.maxY, box.maxZ)).optimize();
+                }
+                case DOWN: // -Y
+                case UP: { // +Y
+                    final boolean useEmpty = direction == Direction.UP ? !DoubleMath.fuzzyEquals(box.maxY, 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) :
+                            !DoubleMath.fuzzyEquals(box.minY, 0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON);
+                    return useEmpty ? empty() : new io.papermc.paper.voxel.AABBVoxelShape(new AABB(box.minX, 0.0, box.minZ, box.maxX, 1.0, box.maxZ)).optimize();
+                }
+                case NORTH: // -Z
+                case SOUTH: { // +Z
+                    final boolean useEmpty = direction == Direction.SOUTH ? !DoubleMath.fuzzyEquals(box.maxZ, 1.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) :
+                            !DoubleMath.fuzzyEquals(box.minZ,0.0, io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON);
+                    return useEmpty ? empty() : new io.papermc.paper.voxel.AABBVoxelShape(new AABB(box.minX, box.minY, 0.0, box.maxX, box.maxY, 1.0)).optimize();
+                }
+            }
+        }
+
+        // fall back to vanilla
+        return getFaceShapeVanilla(shape, direction);
+    }
+    public static VoxelShape getFaceShapeVanilla(VoxelShape shape, Direction direction) {
+        // Paper end
         if (shape == block()) {
             return block();
         } else {
@@ -210,7 +247,7 @@
                 i = 0;
             }
 
-            return (VoxelShape)(!bl ? empty() : new SliceShape(shape, axis, i));
+            return (VoxelShape)(!bl ? empty() : new SliceShape(shape, axis, i).optimize().optimize()); // Paper - first optimize converts to ArrayVoxelShape, second optimize could convert to AABBVoxelShape
         }
     }
 
@@ -235,6 +272,53 @@
     }
 
     public static boolean faceShapeOccludes(VoxelShape one, VoxelShape two) {
+        // Paper start - try to optimise for the case where the shapes do _not_ occlude
+        // which is _most_ of the time in lighting
+        if (one == getFullUnoptimisedCube() || one == BLOCK_OPTIMISED
+                || two == getFullUnoptimisedCube() || two == BLOCK_OPTIMISED) {
+            return true;
+        }
+        boolean v1Empty = one == empty();
+        boolean v2Empty = two == empty();
+        if (v1Empty && v2Empty) {
+            return false;
+        }
+        if ((one instanceof io.papermc.paper.voxel.AABBVoxelShape || v1Empty)
+            && (two instanceof io.papermc.paper.voxel.AABBVoxelShape || v2Empty)) {
+            if (!v1Empty && !v2Empty && (one != two)) {
+                AABB boundingBox1 = ((io.papermc.paper.voxel.AABBVoxelShape)one).aabb;
+                AABB boundingBox2 = ((io.papermc.paper.voxel.AABBVoxelShape)two).aabb;
+                // can call it here in some cases
+
+                // check overall bounding box
+                double minY = Math.min(boundingBox1.minY, boundingBox2.minY);
+                double maxY = Math.max(boundingBox1.maxY, boundingBox2.maxY);
+                if (minY > io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON || maxY < (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) {
+                    return false;
+                }
+                double minX = Math.min(boundingBox1.minX, boundingBox2.minX);
+                double maxX = Math.max(boundingBox1.maxX, boundingBox2.maxX);
+                if (minX > io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON || maxX < (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) {
+                    return false;
+                }
+                double minZ = Math.min(boundingBox1.minZ, boundingBox2.minZ);
+                double maxZ = Math.max(boundingBox1.maxZ, boundingBox2.maxZ);
+                if (minZ > io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON || maxZ < (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) {
+                    return false;
+                }
+                // fall through to full merge check
+            } else {
+                AABB boundingBox = v1Empty ? ((io.papermc.paper.voxel.AABBVoxelShape)two).aabb : ((io.papermc.paper.voxel.AABBVoxelShape)one).aabb;
+                // check if the bounding box encloses the full cube
+                return (boundingBox.minY <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && boundingBox.maxY >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) &&
+                       (boundingBox.minX <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && boundingBox.maxX >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON)) &&
+                       (boundingBox.minZ <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && boundingBox.maxZ >= (1 - io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON));
+            }
+        }
+        return faceShapeOccludesVanilla(one, two);
+    }
+    public static boolean faceShapeOccludesVanilla(VoxelShape one, VoxelShape two) {
+        // Paper end
         if (one != block() && two != block()) {
             if (one.isEmpty() && two.isEmpty()) {
                 return false;
@@ -247,9 +331,21 @@
     }
 
     @VisibleForTesting
-    protected static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
+    private static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) { // Paper - private
+        // Paper start - fast track the most common scenario
+        // doublelist is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause
+        // This is actually the most common path, so jump to it straight away
+        if (first.getDouble(0) == Double.NEGATIVE_INFINITY && first.getDouble(first.size() - 1) == Double.POSITIVE_INFINITY) {
+            return new IndirectMerger(first, second, includeFirst, includeSecond);
+        }
+        // Split out rest to hopefully inline the above
+        return lessCommonMerge(size, first, second, includeFirst, includeSecond);
+    }
+
+    private static IndexMerger lessCommonMerge(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
         int i = first.size() - 1;
         int j = second.size() - 1;
+        // Paper note - Rewrite below as optimized order if instead of nasty ternary
         if (first instanceof CubePointRange && second instanceof CubePointRange) {
             long l = lcm(i, j);
             if ((long)size * l <= 256L) {
@@ -257,13 +353,22 @@
             }
         }
 
-        if (first.getDouble(i) < second.getDouble(0) - 1.0E-7D) {
+        // Paper start - Identical happens more often than Disjoint
+        if (i == j && Objects.equals(first, second)) {
+            if (first instanceof IdenticalMerger) {
+                return (IndexMerger) first;
+            } else if (second instanceof IdenticalMerger) {
+                return (IndexMerger) second;
+            }
+            return new IdenticalMerger(first);
+        } else if (first.getDouble(i) < second.getDouble(0) - 1.0E-7D) {
             return new NonOverlappingMerger(first, second, false);
         } else if (second.getDouble(j) < first.getDouble(0) - 1.0E-7D) {
             return new NonOverlappingMerger(second, first, true);
         } else {
-            return (IndexMerger)(i == j && Objects.equals(first, second) ? new IdenticalMerger(first) : new IndirectMerger(first, second, includeFirst, includeSecond));
+            return new IndirectMerger(first, second, includeFirst, includeSecond);
         }
+        // Paper end
     }
 
     public interface DoubleLineConsumer {
