Browse Source

Fix #103, Fix #105, Fix #99, Fix #100

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 4 years ago
parent
commit
ccbc35ff0f

+ 242 - 0
common/src/main/java/me/shedaniel/lightoverlay/common/ChunkData.java

@@ -0,0 +1,242 @@
+package me.shedaniel.lightoverlay.common;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.*;
+import com.mojang.datafixers.util.Pair;
+import com.mojang.math.Vector3f;
+import it.unimi.dsi.fastutil.longs.Long2ByteMap;
+import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.RenderType;
+import net.minecraft.client.renderer.ShaderInstance;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ChunkData implements Closeable {
+    private static final Minecraft minecraft = Minecraft.getInstance();
+    @Nullable
+    public static ShaderInstance lightoverlayPositionColorShaderWithWidth;
+    private final Long2ByteMap data;
+    private Iterable<Pair<RenderType, VertexBuffer>> buffers;
+    
+    public ChunkData() {
+        this(new Long2ByteOpenHashMap());
+    }
+    
+    public ChunkData(Long2ByteMap data) {
+        this.data = data;
+    }
+    
+    public Long2ByteMap data() {
+        return data;
+    }
+    
+    @Override
+    public void close() {
+        if (!RenderSystem.isOnRenderThread()) {
+            RenderSystem.recordRenderCall(this::_close);
+        } else {
+            _close();
+        }
+    }
+    
+    private void _close() {
+        if (buffers != null) {
+            for (Pair<RenderType, VertexBuffer> buffer : buffers) {
+                buffer.getSecond().close();
+            }
+            buffers = null;
+        }
+    }
+    
+    public void put(long l, byte level) {
+        this.data.put(l, level);
+    }
+    
+    public static void render(PoseStack poses) {
+        if (!LightOverlay.enabled) return;
+        var playerEntity = minecraft.player;
+        var playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
+        var playerPosX = playerPos.getX() >> 4;
+        var playerPosY = playerPos.getY() >> 5;
+        var playerPosZ = playerPos.getZ() >> 4;
+        var collisionContext = CollisionContext.of(playerEntity);
+        var camera = minecraft.gameRenderer.getMainCamera();
+        var chunkRange = LightOverlay.getChunkRange();
+        var ticker = LightOverlay.ticker;
+        
+        RenderSystem.enableDepthTest();
+        RenderSystem.disableTexture();
+        RenderSystem.disableBlend();
+        RenderSystem.depthMask(false);
+        var p = new PoseStack();
+        poses.pushPose();
+        p.mulPoseMatrix(RenderSystem.getProjectionMatrix());
+        poses.translate(-camera.getPosition().x(), -camera.getPosition().y(), -camera.getPosition().z());
+        
+        for (var entry : ticker.CHUNK_MAP.entrySet()) {
+            var chunkPos = entry.getKey();
+            var data = entry.getValue();
+            if ((Mth.abs(chunkPos.x - playerPosX) > chunkRange || Mth.abs(chunkPos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(chunkPos.z - playerPosZ) > chunkRange)) {
+                continue;
+            }
+            if (!LightOverlay.renderer.isFrustumVisible(chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(),
+                    chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) {
+                continue;
+            }
+            var buffers = data.buffer(collisionContext);
+            for (Pair<RenderType, VertexBuffer> pair : buffers) {
+                RenderType type = pair.getFirst();
+                ShaderInstance shader = GameRenderer.getPositionColorShader();
+                if (LightOverlay.showNumber) {
+                    type.setupRenderState();
+                    shader = RenderSystem.getShader();
+                } else {
+//                    RenderSystem.lineWidth(LightOverlay.lineWidth);
+//                    if (shader.LINE_WIDTH != null) {
+//                        shader.LINE_WIDTH.set(RenderSystem.getShaderLineWidth());
+//                    }
+                }
+                VertexBuffer buffer = pair.getSecond();
+                buffer.drawWithShader(poses.last().pose(), p.last().pose(), shader);
+                if (LightOverlay.showNumber) {
+                    type.clearRenderState();
+                }
+            }
+            data._close();
+        }
+        
+        poses.popPose();
+        RenderSystem.lineWidth(1.0F);
+        RenderSystem.depthMask(true);
+        RenderSystem.enableTexture();
+        RenderSystem.enableBlend();
+    }
+    
+    private Iterable<Pair<RenderType, VertexBuffer>> buffer(CollisionContext collisionContext) {
+        if (this.buffers == null) {
+            if (!LightOverlay.showNumber) {
+                var buffer = new VertexBuffer();
+                var tesselator = Tesselator.getInstance();
+                var builder = tesselator.getBuilder();
+                this.renderCrosses(builder, collisionContext);
+                builder.end();
+                buffer.upload(builder);
+                this.buffers = Collections.singleton(Pair.of(null, buffer));
+            } else {
+                var buffers = new ArrayList<Pair<RenderType, VertexBuffer>>();
+                var types = new HashMap<RenderType, BufferBuilder>();
+                this.renderLevels(renderType -> types.computeIfAbsent(renderType, type -> {
+                    BufferBuilder builder = new BufferBuilder(256);
+                    builder.begin(type.mode(), type.format());
+                    return builder;
+                }), collisionContext);
+                for (Map.Entry<RenderType, BufferBuilder> entry : types.entrySet()) {
+                    var buffer = new VertexBuffer();
+                    entry.getValue().end();
+                    buffer.upload(entry.getValue());
+                    buffers.add(Pair.of(entry.getKey(), buffer));
+                }
+                this.buffers = buffers;
+            }
+        }
+        
+        return this.buffers;
+    }
+    
+    private void renderCrosses(BufferBuilder builder, CollisionContext collisionContext) {
+        float lineWidth = LightOverlay.lineWidth * 12 / Mth.sqrt(Minecraft.getInstance().getWindow().getGuiScaledWidth() * Minecraft.getInstance().getWindow().getGuiScaledHeight());
+//        builder.begin(VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR);
+        builder.begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION_COLOR);
+        var mutable = new BlockPos.MutableBlockPos();
+        
+        for (var objectEntry : data().long2ByteEntrySet()) {
+            mutable.set(objectEntry.getLongKey());
+            var color = switch (objectEntry.getByteValue()) {
+                case LightOverlay.CROSS_RED -> LightOverlay.redColor;
+                case LightOverlay.CROSS_YELLOW -> LightOverlay.yellowColor;
+                default -> LightOverlay.secondaryColor;
+            };
+            renderCross(builder, minecraft.level, mutable, color, collisionContext, lineWidth);
+        }
+    }
+    
+    private void renderLevels(MultiBufferSource source, CollisionContext collisionContext) {
+        var mutable = new BlockPos.MutableBlockPos();
+        var mutableDown = new BlockPos.MutableBlockPos();
+        var poses = new PoseStack();
+        
+        for (var objectEntry : data().long2ByteEntrySet()) {
+            mutable.set(objectEntry.getLongKey());
+            var x = mutable.getX();
+            var y = mutable.getY();
+            var z = mutable.getZ();
+            mutableDown.set(x, y - 1, z);
+            var level = objectEntry.getByteValue();
+            renderLevel(poses, source, minecraft.level, mutable, mutableDown, level, collisionContext);
+        }
+    }
+    
+    public void renderCross(BufferBuilder builder, Level level, BlockPos pos, int color, CollisionContext collisionContext, double lineWidth) {
+        var blockOffset = .005D;
+        var upperOutlineShape = level.getBlockState(pos).getShape(level, pos, collisionContext);
+        if (!upperOutlineShape.isEmpty()) {
+            blockOffset += upperOutlineShape.max(Direction.Axis.Y);
+        }
+        
+        var red = (color >> 16) & 255;
+        var green = (color >> 8) & 255;
+        var blue = color & 255;
+        var x = pos.getX();
+        var y = pos.getY() + blockOffset;
+        var z = pos.getZ();
+        line(builder, x, z, x + 1, z + 1, y, lineWidth, lineWidth, red, green, blue, 255);
+        line(builder, x + 1, z, x, z + 1, y, lineWidth, lineWidth, red, green, blue, 255);
+    }
+    
+    public void line(VertexConsumer vertexConsumer, double x1, double z1, double x2, double z2, double y, double t1, double t2, int red, int green, int blue, int alpha) {
+        float angle = (float) Mth.atan2(z2 - z1, x2 - x1);
+        float sinA = Mth.sin(angle);
+        float cosA = Mth.cos(angle);
+        double t2sina1 = t1 / 2 * sinA;
+        double t2cosa1 = t1 / 2 * cosA;
+        double t2sina2 = t2 / 2 * sinA;
+        double t2cosa2 = t2 / 2 * cosA;
+        
+        vertexConsumer.vertex(x1 + t2sina1, y, z1 - t2cosa1).color(red, green, blue, alpha).endVertex();
+        vertexConsumer.vertex(x2 - t2sina2, y, z2 + t2cosa2).color(red, green, blue, alpha).endVertex();
+        vertexConsumer.vertex(x2 + t2sina2, y, z2 - t2cosa2).color(red, green, blue, alpha).endVertex();
+        vertexConsumer.vertex(x2 - t2sina2, y, z2 + t2cosa2).color(red, green, blue, alpha).endVertex();
+        vertexConsumer.vertex(x1 + t2sina1, y, z1 - t2cosa1).color(red, green, blue, alpha).endVertex();
+        vertexConsumer.vertex(x1 - t2sina1, y, z1 + t2cosa1).color(red, green, blue, alpha).endVertex();
+    }
+    
+    public void renderLevel(PoseStack poses, MultiBufferSource source, Level level, BlockPos pos, BlockPos down, byte levelLevel, CollisionContext collisionContext) {
+        var text = String.valueOf(levelLevel);
+        var font = minecraft.font;
+        var cameraY = -0.005;
+        var upperOutlineShape = level.getBlockState(down).getShape(level, down, collisionContext);
+        if (!upperOutlineShape.isEmpty())
+            cameraY += 1 - upperOutlineShape.max(Direction.Axis.Y);
+        poses.pushPose();
+        poses.translate(pos.getX() + 0.5, pos.getY() - cameraY, pos.getZ() + 0.5);
+        poses.mulPose(Vector3f.XP.rotationDegrees(90));
+        var size = 0.07F;
+        poses.scale(-size, -size, size);
+        var x = (float) (-font.width(text)) / 2.0F + 0.4f;
+        font.drawInBatch(text, x, -3.5f, levelLevel > LightOverlay.higherCrossLevel ? 0xff042404 : (LightOverlay.lowerCrossLevel >= 0 && levelLevel > LightOverlay.lowerCrossLevel ? 0xff0066ff : 0xff731111), false, poses.last().pose(), source, false, 0, 0xf000f0);
+        poses.popPose();
+    }
+}

+ 1 - 2
common/src/main/java/me/shedaniel/lightoverlay/common/ClothScreen.java

@@ -17,8 +17,7 @@ public class ClothScreen {
         var eb = builder.entryBuilder();
         var general = builder.getOrCreateCategory(new TranslatableComponent("config.lightoverlay.general"));
         general.addEntry(eb.startTextDescription(Component.nullToEmpty("§7" + I18n.get("description.lightoverlay.caching"))).build());
-        general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.caching"), LightOverlay.caching).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.caching = bool).build());
-        general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.reach"), LightOverlay.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> Component.nullToEmpty("Reach: " + integer + " Blocks")).setSaveConsumer(integer -> LightOverlay.reach = integer).build());
+        general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.reach"), LightOverlay.reach, 1, 512).setDefaultValue(12).setTextGetter(integer -> Component.nullToEmpty("Reach: " + integer + " Blocks")).setSaveConsumer(integer -> LightOverlay.reach = integer).build());
         var crossLevel = eb.startIntSlider(new TranslatableComponent("config.lightoverlay.crossLevel"), LightOverlay.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> Component.nullToEmpty("Cross Level: " + integer)).setSaveConsumer(integer -> LightOverlay.crossLevel = integer).build();
         general.addEntry(crossLevel);
         general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.secondaryLevel"), LightOverlay.secondaryLevel, -1, 15)

+ 0 - 9
common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlay.java

@@ -23,7 +23,6 @@ public class LightOverlay {
     public static int secondaryLevel = -1;
     public static int lowerCrossLevel = -1;
     public static int higherCrossLevel = -1;
-    public static boolean caching = false;
     public static boolean showNumber = false;
     public static boolean underwater = false;
     public static float lineWidth = 1.0F;
@@ -47,11 +46,7 @@ public class LightOverlay {
         
         ClientGuiEvent.DEBUG_TEXT_LEFT.register(list -> {
             if (enabled) {
-                if (caching) {
                     list.add(String.format("[Light Overlay] Chunks to queue: %02d; %d Blocks Scanned", ticker.POS.size(), blocksScanned));
-                } else {
-                    list.add(String.format("[Light Overlay] Enabled; %d Blocks Scanned", blocksScanned));
-                }
             } else {
                 list.add("[Light Overlay] Disabled");
             }
@@ -93,7 +88,6 @@ public class LightOverlay {
             reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
             crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
             secondaryLevel = Integer.parseInt((String) properties.computeIfAbsent("secondaryLevel", a -> "-1"));
-            caching = ((String) properties.computeIfAbsent("caching", a -> "false")).equalsIgnoreCase("true");
             showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
             underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
             lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
@@ -128,7 +122,6 @@ public class LightOverlay {
             redColor = 0xFF0000;
             yellowColor = 0xFFFF00;
             secondaryColor = 0x0000FF;
-            caching = false;
             showNumber = false;
             underwater = false;
             try {
@@ -154,8 +147,6 @@ public class LightOverlay {
         fos.write("\n".getBytes());
         fos.write(("secondaryLevel=" + secondaryLevel).getBytes());
         fos.write("\n".getBytes());
-        fos.write(("caching=" + caching).getBytes());
-        fos.write("\n".getBytes());
         fos.write(("showNumber=" + showNumber).getBytes());
         fos.write("\n".getBytes());
         fos.write(("underwater=" + underwater).getBytes());

+ 2 - 132
common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayRenderer.java

@@ -1,19 +1,8 @@
 package me.shedaniel.lightoverlay.common;
 
-import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.*;
-import com.mojang.math.Matrix4f;
-import com.mojang.math.Vector3f;
-import net.minecraft.client.Camera;
+import com.mojang.blaze3d.vertex.PoseStack;
 import net.minecraft.client.Minecraft;
-import net.minecraft.client.renderer.GameRenderer;
-import net.minecraft.client.renderer.MultiBufferSource;
 import net.minecraft.client.renderer.culling.Frustum;
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.Direction;
-import net.minecraft.util.Mth;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.phys.shapes.CollisionContext;
 
 import java.util.function.Consumer;
 
@@ -30,129 +19,10 @@ public class LightOverlayRenderer implements Consumer<PoseStack> {
     @Override
     public void accept(PoseStack poses) {
         if (LightOverlay.enabled) {
-            var playerEntity = minecraft.player;
-            var playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
-            var playerPosX = playerPos.getX() >> 4;
-            var playerPosY = playerPos.getY() >> 5;
-            var playerPosZ = playerPos.getZ() >> 4;
-            var collisionContext = CollisionContext.of(playerEntity);
-            var camera = minecraft.gameRenderer.getMainCamera();
-            var chunkRange = LightOverlay.getChunkRange();
-            
-            if (LightOverlay.showNumber) {
-                renderLevels(new PoseStack(), camera, playerPos, playerPosX, playerPosY, playerPosZ, chunkRange, collisionContext);
-            } else {
-                renderCrosses(poses, camera, playerPos, playerPosX, playerPosY, playerPosZ, chunkRange, collisionContext);
-            }
+            ChunkData.render(poses);
         }
     }
     
-    private void renderLevels(PoseStack poses, Camera camera, BlockPos playerPos, int playerPosX, int playerPosY, int playerPosZ, int chunkRange, CollisionContext collisionContext) {
-        RenderSystem.enableTexture();
-        RenderSystem.depthMask(true);
-        var mutable = new BlockPos.MutableBlockPos();
-        var downMutable = new BlockPos.MutableBlockPos();
-        var source = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
-        for (var entry : ticker.CHUNK_MAP.entrySet()) {
-            var chunkPos = entry.getKey();
-            if (LightOverlay.caching && (Mth.abs(chunkPos.x - playerPosX) > chunkRange || Mth.abs(chunkPos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(chunkPos.z - playerPosZ) > chunkRange)) {
-                continue;
-            }
-            for (var objectEntry : entry.getValue().long2ByteEntrySet()) {
-                mutable.set(objectEntry.getLongKey());
-                if (mutable.closerThan(playerPos, LightOverlay.reach)) {
-                    if (isFrustumVisible(mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) {
-                        downMutable.set(mutable.getX(), mutable.getY() - 1, mutable.getZ());
-                        renderLevel(poses, source, camera, minecraft.level, mutable, downMutable, objectEntry.getByteValue(), collisionContext);
-                    }
-                }
-            }
-        }
-        RenderSystem.enableDepthTest();
-        source.endBatch();
-    }
-    
-    public void renderLevel(PoseStack poses, MultiBufferSource.BufferSource source, Camera camera, Level world, BlockPos pos, BlockPos down, byte level, CollisionContext collisionContext) {
-        var text = String.valueOf(level);
-        var font = minecraft.font;
-        var cameraX = camera.getPosition().x;
-        var cameraY = camera.getPosition().y;
-        var upperOutlineShape = world.getBlockState(down).getShape(world, down, collisionContext);
-        if (!upperOutlineShape.isEmpty())
-            cameraY += 1 - upperOutlineShape.max(Direction.Axis.Y);
-        var cameraZ = camera.getPosition().z;
-        poses.pushPose();
-        poses.translate(pos.getX() + 0.5 - cameraX, pos.getY() - cameraY + 0.005, pos.getZ() + 0.5 - cameraZ);
-        poses.mulPose(Vector3f.XP.rotationDegrees(90));
-//        poses.glNormal3f(0.0F, 1.0F, 0.0F);
-        var size = 0.07F;
-        poses.scale(-size, -size, size);
-        var float_3 = (float) (-font.width(text)) / 2.0F + 0.4f;
-        font.drawInBatch(text, float_3, -3.5f, level > LightOverlay.higherCrossLevel ? 0xff042404 : (LightOverlay.lowerCrossLevel >= 0 && level > LightOverlay.lowerCrossLevel ? 0xff0066ff : 0xff731111), false, poses.last().pose(), source, false, 0, 15728880);
-        poses.popPose();
-    }
-    
-    private void renderCrosses(PoseStack poses, Camera camera, BlockPos playerPos, int playerPosX, int playerPosY, int playerPosZ, int chunkRange, CollisionContext collisionContext) {
-        RenderSystem.enableDepthTest();
-        RenderSystem.disableTexture();
-        RenderSystem.disableBlend();
-        RenderSystem.setShader(GameRenderer::getPositionColorShader);
-        RenderSystem.lineWidth(LightOverlay.lineWidth);
-        var tesselator = Tesselator.getInstance();
-        var builder = tesselator.getBuilder();
-        builder.begin(VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR);
-        var mutable = new BlockPos.MutableBlockPos();
-        
-        for (var entry : ticker.CHUNK_MAP.entrySet()) {
-            var chunkPos = entry.getKey();
-            if (LightOverlay.caching && (Mth.abs(chunkPos.x - playerPosX) > chunkRange || Mth.abs(chunkPos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(chunkPos.z - playerPosZ) > chunkRange)) {
-                continue;
-            }
-            
-            for (var objectEntry : entry.getValue().long2ByteEntrySet()) {
-                var crossType = objectEntry.getByteValue();
-                mutable.set(objectEntry.getLongKey());
-                if (mutable.closerThan(playerPos, LightOverlay.reach)) {
-                    if (isFrustumVisible(mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) {
-                        var color = switch (crossType) {
-                            case LightOverlay.CROSS_RED -> LightOverlay.redColor;
-                            case LightOverlay.CROSS_YELLOW -> LightOverlay.yellowColor;
-                            default -> LightOverlay.secondaryColor;
-                        };
-                        renderCross(poses.last().pose(), builder, camera, minecraft.level, mutable, color, collisionContext);
-                    }
-                }
-            }
-        }
-        
-        tesselator.end();
-        RenderSystem.lineWidth(1.0F);
-        RenderSystem.enableBlend();
-        RenderSystem.enableTexture();
-    }
-    
-    public void renderCross(Matrix4f pose, BufferBuilder builder, Camera camera, Level world, BlockPos pos, int color, CollisionContext collisionContext) {
-        var cameraX = camera.getPosition().x;
-        var cameraY = camera.getPosition().y - .005D;
-        double blockOffset = 0;
-        var upperOutlineShape = world.getBlockState(pos).getShape(world, pos, collisionContext);
-        if (!upperOutlineShape.isEmpty()) {
-            blockOffset += upperOutlineShape.max(Direction.Axis.Y);
-        }
-        var cameraZ = camera.getPosition().z;
-        
-        var red = (color >> 16) & 255;
-        var green = (color >> 8) & 255;
-        var blue = color & 255;
-        var x = pos.getX() - cameraX;
-        var y = pos.getY() - cameraY + blockOffset;
-        var z = pos.getZ() - cameraZ;
-        builder.vertex(x + .01, y, z + .01).color(red, green, blue, 255).endVertex();
-        builder.vertex(x + .99, y, z + .99).color(red, green, blue, 255).endVertex();
-        builder.vertex(x + .99, y, z + .01).color(red, green, blue, 255).endVertex();
-        builder.vertex(x + .01, y, z + .99).color(red, green, blue, 255).endVertex();
-    }
-    
     public boolean isFrustumVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
         try {
             return frustum == null || frustumAccess.isVisible(frustum, minX, minY, minZ, maxX, maxY, maxZ);

+ 96 - 118
common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayTicker.java

@@ -1,10 +1,7 @@
 package me.shedaniel.lightoverlay.common;
 
-import com.google.common.base.Stopwatch;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.Maps;
-import it.unimi.dsi.fastutil.longs.Long2ByteMap;
-import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
 import net.minecraft.client.Minecraft;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
@@ -26,10 +23,7 @@ import net.minecraft.world.phys.shapes.CollisionContext;
 import net.minecraft.world.phys.shapes.Shapes;
 import org.apache.logging.log4j.LogManager;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.function.Supplier;
@@ -45,12 +39,12 @@ public class LightOverlayTicker {
     });
     public final Set<CubicChunkPos> POS = Collections.synchronizedSet(new HashSet<>());
     public final Set<CubicChunkPos> CALCULATING_POS = Collections.synchronizedSet(new HashSet<>());
-    public final Map<CubicChunkPos, Long2ByteMap> CHUNK_MAP = Maps.newConcurrentMap();
+    public final Map<CubicChunkPos, ChunkData> CHUNK_MAP = Maps.newConcurrentMap();
     private static final Supplier<EntityType<Entity>> TESTING_ENTITY_TYPE = Suppliers.memoize(() ->
             EntityType.Builder.createNothing(MobCategory.MONSTER).sized(0f, 0f).noSave().build(null));
     
     public void queueChunk(CubicChunkPos pos) {
-        if (LightOverlay.enabled && LightOverlay.caching && !CALCULATING_POS.contains(pos)) {
+        if (LightOverlay.enabled && !CALCULATING_POS.contains(pos)) {
             if (minecraft.level != null) {
                 var chunk = minecraft.level.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
                 if (chunk == null) return;
@@ -83,123 +77,104 @@ public class LightOverlayTicker {
                 POS.clear();
                 CALCULATING_POS.clear();
                 EXECUTOR.getQueue().clear();
+                for (ChunkData data : CHUNK_MAP.values()) {
+                    data.close();
+                }
                 CHUNK_MAP.clear();
             } else {
                 var player = minecraft.player;
                 var world = minecraft.level;
                 var collisionContext = CollisionContext.of(player);
                 
-                if (!LightOverlay.caching) {
-                    CALCULATING_POS.clear();
-                    POS.clear();
-                    CHUNK_MAP.clear();
-                    var playerPos = player.blockPosition();
-                    var block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
-                    var sky = LightOverlay.showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
-                    var downPos = new BlockPos.MutableBlockPos();
-                    var iterate = BlockPos.betweenClosed(playerPos.getX() - LightOverlay.reach, playerPos.getY() - LightOverlay.reach, playerPos.getZ() - LightOverlay.reach,
-                            playerPos.getX() + LightOverlay.reach, playerPos.getY() + LightOverlay.reach, playerPos.getZ() + LightOverlay.reach);
-                    Long2ByteMap chunkData = new Long2ByteOpenHashMap();
-                    CHUNK_MAP.put(new CubicChunkPos(0, 0, 0), chunkData);
-                    for (var blockPos : iterate) {
-                        downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
-//                        if (LightOverlay.showNumber) {
-//                            var level = getCrossLevel(blockPos, downPos, world, block, collisionContext);
-//                            if (level >= 0) {
-//                                chunkData.put(blockPos.asLong(), (byte) level);
-//                            }
-//                        } else {
-//                            var biome = !LightOverlay.mushroom ? world.getBiome(blockPos) : null;
-//                            var type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext);
-//                            if (type != LightOverlay.CROSS_NONE) {
-//                                chunkData.put(blockPos.asLong(), type);
-//                            }
-//                        }
-                    }
-                } else {
-                    var height = Mth.ceil(Minecraft.getInstance().level.getHeight() / 32.0);
-                    var start = Math.floorDiv(Minecraft.getInstance().level.getMinBuildHeight(), 32);
-                    var playerPosX = ((int) player.getX()) >> 4;
-                    var playerPosY = ((int) player.getY()) >> 5;
-                    var playerPosZ = ((int) player.getZ()) >> 4;
-                    var chunkRange = LightOverlay.getChunkRange();
-                    for (var chunkX = playerPosX - chunkRange; chunkX <= playerPosX + chunkRange; chunkX++) {
-                        for (var chunkY = Math.max(playerPosY - Math.max(1, chunkRange >> 1), start); chunkY <= playerPosY + Math.max(1, chunkRange >> 1) && chunkY <= start + height; chunkY++) {
-                            for (var chunkZ = playerPosZ - chunkRange; chunkZ <= playerPosZ + chunkRange; chunkZ++) {
-                                if (Mth.abs(chunkX - playerPosX) > chunkRange || Mth.abs(chunkY - playerPosY) > chunkRange || Mth.abs(chunkZ - playerPosZ) > chunkRange)
-                                    continue;
-                                var chunkPos = new CubicChunkPos(chunkX, chunkY, chunkZ);
-                                if (!CHUNK_MAP.containsKey(chunkPos))
-                                    queueChunk(chunkPos);
-                            }
+                var height = Mth.ceil(Minecraft.getInstance().level.getHeight() / 32.0);
+                var start = Math.floorDiv(Minecraft.getInstance().level.getMinBuildHeight(), 32);
+                var playerPosX = ((int) player.getX()) >> 4;
+                var playerPosY = ((int) player.getY()) >> 5;
+                var playerPosZ = ((int) player.getZ()) >> 4;
+                var chunkRange = LightOverlay.getChunkRange();
+                for (var chunkX = playerPosX - chunkRange; chunkX <= playerPosX + chunkRange; chunkX++) {
+                    for (var chunkY = Math.max(playerPosY - Math.max(1, chunkRange >> 1), start); chunkY <= playerPosY + Math.max(1, chunkRange >> 1) && chunkY <= start + height; chunkY++) {
+                        for (var chunkZ = playerPosZ - chunkRange; chunkZ <= playerPosZ + chunkRange; chunkZ++) {
+                            if (Mth.abs(chunkX - playerPosX) > chunkRange || Mth.abs(chunkY - playerPosY) > chunkRange || Mth.abs(chunkZ - playerPosZ) > chunkRange)
+                                continue;
+                            var chunkPos = new CubicChunkPos(chunkX, chunkY, chunkZ);
+                            if (!CHUNK_MAP.containsKey(chunkPos))
+                                queueChunk(chunkPos);
                         }
                     }
-                    for (var p = 0; p < 3; p++) {
-                        if (EXECUTOR.getQueue().size() >= Runtime.getRuntime().availableProcessors()) break;
-                        double d1 = Double.MAX_VALUE, d2 = Double.MAX_VALUE, d3 = Double.MAX_VALUE;
-                        CubicChunkPos c1 = null, c2 = null, c3 = null;
-                        synchronized (POS) {
-                            var iterator = POS.iterator();
-                            while (iterator.hasNext()) {
-                                var pos = iterator.next();
-                                if (Mth.abs(pos.x - playerPosX) > chunkRange || Mth.abs(pos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(pos.z - playerPosZ) > chunkRange || CALCULATING_POS.contains(pos)) {
-                                    iterator.remove();
-                                } else {
-                                    if (LightOverlay.renderer.isFrustumVisible(pos.getMinBlockX(), pos.getMinBlockY(), pos.getMinBlockZ(), pos.getMaxBlockX(), pos.getMaxBlockY(), pos.getMaxBlockZ())) {
-                                        var dx = Math.abs(pos.x - playerPosX);
-                                        var dy = Math.abs(pos.y - playerPosY) << 1;
-                                        var dz = Math.abs(pos.z - playerPosZ);
-                                        var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
-                                        if (distance < d1) {
-                                            d3 = d2;
-                                            d2 = d1;
-                                            d1 = distance;
-                                            c3 = c2;
-                                            c2 = c1;
-                                            c1 = pos;
-                                        } else if (distance < d2) {
-                                            d3 = d2;
-                                            d2 = distance;
-                                            c3 = c2;
-                                            c2 = pos;
-                                        } else if (distance < d3) {
-                                            d3 = distance;
-                                            c3 = pos;
-                                        }
+                }
+                for (var p = 0; p < 3; p++) {
+                    if (EXECUTOR.getQueue().size() >= Runtime.getRuntime().availableProcessors()) break;
+                    double d1 = Double.MAX_VALUE, d2 = Double.MAX_VALUE, d3 = Double.MAX_VALUE;
+                    CubicChunkPos c1 = null, c2 = null, c3 = null;
+                    synchronized (POS) {
+                        var iterator = POS.iterator();
+                        while (iterator.hasNext()) {
+                            var pos = iterator.next();
+                            if (Mth.abs(pos.x - playerPosX) > chunkRange || Mth.abs(pos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(pos.z - playerPosZ) > chunkRange || CALCULATING_POS.contains(pos)) {
+                                iterator.remove();
+                            } else {
+                                if (LightOverlay.renderer.isFrustumVisible(pos.getMinBlockX(), pos.getMinBlockY(), pos.getMinBlockZ(), pos.getMaxBlockX(), pos.getMaxBlockY(), pos.getMaxBlockZ())) {
+                                    var dx = Math.abs(pos.x - playerPosX);
+                                    var dy = Math.abs(pos.y - playerPosY) << 1;
+                                    var dz = Math.abs(pos.z - playerPosZ);
+                                    var distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
+                                    if (distance < d1) {
+                                        d3 = d2;
+                                        d2 = d1;
+                                        d1 = distance;
+                                        c3 = c2;
+                                        c2 = c1;
+                                        c1 = pos;
+                                    } else if (distance < d2) {
+                                        d3 = d2;
+                                        d2 = distance;
+                                        c3 = c2;
+                                        c2 = pos;
+                                    } else if (distance < d3) {
+                                        d3 = distance;
+                                        c3 = pos;
                                     }
                                 }
                             }
                         }
-                        var finalC1 = c1;
-                        var finalC2 = c2;
-                        var finalC3 = c3;
-                        if (finalC1 != null) {
-                            CALCULATING_POS.add(finalC1);
-                            POS.remove(finalC1);
-                            if (finalC2 != null) {
-                                CALCULATING_POS.add(finalC2);
-                                POS.remove(finalC2);
-                                if (finalC3 != null) {
-                                    CALCULATING_POS.add(finalC3);
-                                    POS.remove(finalC3);
-                                }
+                    }
+                    var finalC1 = c1;
+                    var finalC2 = c2;
+                    var finalC3 = c3;
+                    if (finalC1 != null) {
+                        CALCULATING_POS.add(finalC1);
+                        POS.remove(finalC1);
+                        if (finalC2 != null) {
+                            CALCULATING_POS.add(finalC2);
+                            POS.remove(finalC2);
+                            if (finalC3 != null) {
+                                CALCULATING_POS.add(finalC3);
+                                POS.remove(finalC3);
                             }
-                            EXECUTOR.submit(() -> {
-                                var playerPosX1 = ((int) minecraft.player.getX()) >> 4;
-                                var playerPosY1 = ((int) minecraft.player.getY()) >> 5;
-                                var playerPosZ1 = ((int) minecraft.player.getZ()) >> 4;
-                                var count = 0;
-                                if (finalC1 != null) count += processChunk(finalC1, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
-                                if (finalC2 != null) count += processChunk(finalC2, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
-                                if (finalC3 != null) count += processChunk(finalC3, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
-                                synchronized (this) {
-                                    LightOverlay.blocksScanned += count;
-                                }
-                            });
                         }
+                        EXECUTOR.submit(() -> {
+                            var playerPosX1 = ((int) minecraft.player.getX()) >> 4;
+                            var playerPosY1 = ((int) minecraft.player.getY()) >> 5;
+                            var playerPosZ1 = ((int) minecraft.player.getZ()) >> 4;
+                            var count = 0;
+                            if (finalC1 != null) count += processChunk(finalC1, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
+                            if (finalC2 != null) count += processChunk(finalC2, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
+                            if (finalC3 != null) count += processChunk(finalC3, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
+                            synchronized (this) {
+                                LightOverlay.blocksScanned += count;
+                            }
+                        });
                     }
-                    if (ticks % 50 == 0) {
-                        CHUNK_MAP.entrySet().removeIf(entry -> Mth.abs(entry.getKey().x - playerPosX) > chunkRange * 2 || Mth.abs(entry.getKey().y - playerPosY) > chunkRange * 2 || Mth.abs(entry.getKey().z - playerPosZ) > chunkRange * 2);
+                }
+                if (ticks % 50 == 0) {
+                    Iterator<Map.Entry<CubicChunkPos, ChunkData>> iterator = CHUNK_MAP.entrySet().iterator();
+                    while (iterator.hasNext()) {
+                        Map.Entry<CubicChunkPos, ChunkData> entry = iterator.next();
+                        if (Mth.abs(entry.getKey().x - playerPosX) > chunkRange * 2 || Mth.abs(entry.getKey().y - playerPosY) > chunkRange * 2 || Mth.abs(entry.getKey().z - playerPosZ) > chunkRange * 2) {
+                            entry.getValue().close();
+                            iterator.remove();
+                        }
                     }
                 }
             }
@@ -214,13 +189,10 @@ public class LightOverlayTicker {
         if (Mth.abs(pos.x - playerPosX) > chunkRange || Mth.abs(pos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(pos.z - playerPosZ) > chunkRange || POS.contains(pos)) {
             return 0;
         }
-        var stopwatch = Stopwatch.createStarted();
         try {
             return calculateChunk(minecraft.level.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), minecraft.level, pos, context);
         } catch (Throwable throwable) {
             LogManager.getLogger().throwing(throwable);
-        } finally {
-            System.out.println(stopwatch.stop());
         }
         return 0;
     }
@@ -229,21 +201,27 @@ public class LightOverlayTicker {
         if (world != null && chunk != null) {
             var block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
             var sky = LightOverlay.showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
-            Long2ByteMap chunkData = new Long2ByteOpenHashMap();
+            ChunkData chunkData = new ChunkData();
             var count = 0;
             var sections = ((LevelChunkAccess) chunk).lightoverlay_getSections();
             var firstSectionIndex = chunkPos.y << 1 - chunk.getMinSection();
             calculateSection(chunkData, block, sky, world, chunk, sections, firstSectionIndex, chunkPos, 0, collisionContext);
             calculateSection(chunkData, block, sky, world, chunk, sections, firstSectionIndex + 1, chunkPos, 16, collisionContext);
-            CHUNK_MAP.put(chunkPos, chunkData);
+            var data = CHUNK_MAP.put(chunkPos, chunkData);
+            if (data != null) {
+                data.close();
+            }
             return count;
         } else {
-            CHUNK_MAP.remove(chunkPos);
+            var data = CHUNK_MAP.remove(chunkPos);
+            if (data != null) {
+                data.close();
+            }
             return 0;
         }
     }
     
-    private void calculateSection(Long2ByteMap chunkData, LayerLightEventListener block, LayerLightEventListener sky, Level world, LevelChunk chunk, LevelChunkSection[] sections,
+    private void calculateSection(ChunkData chunkData, LayerLightEventListener block, LayerLightEventListener sky, Level world, LevelChunk chunk, LevelChunkSection[] sections,
             int sectionIndex, CubicChunkPos chunkPos, int yOffset, CollisionContext collisionContext) {
         if (sectionIndex >= 0 && sectionIndex < sections.length) {
             var section = sections[sectionIndex];

+ 1 - 1
gradle.properties

@@ -1,7 +1,7 @@
 org.gradle.jvmargs=-Xmx3G
 org.gradle.daemon=false
 
-mod_version=6.0.0
+mod_version=6.1.0
 minecraft_version=1.17
 
 architectury_version=2.0.9