]> git.lizzy.rs Git - LightOverlay.git/blobdiff - common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayRenderer.java
Update to 1.17
[LightOverlay.git] / common / src / main / java / me / shedaniel / lightoverlay / common / LightOverlayRenderer.java
diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayRenderer.java b/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayRenderer.java
new file mode 100644 (file)
index 0000000..7204963
--- /dev/null
@@ -0,0 +1,183 @@
+package me.shedaniel.lightoverlay.common;
+
+import com.google.common.base.Suppliers;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.*;
+import com.mojang.math.Matrix4f;
+import com.mojang.math.Vector3f;
+import dev.architectury.injectables.targets.ArchitecturyTarget;
+import it.unimi.dsi.fastutil.longs.Long2ByteMap;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Font;
+import net.minecraft.client.player.LocalPlayer;
+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 net.minecraft.world.phys.shapes.VoxelShape;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class LightOverlayRenderer implements Consumer<PoseStack> {
+    private final Minecraft minecraft = Minecraft.getInstance();
+    public Frustum frustum;
+    public LightOverlayTicker ticker;
+    
+    public LightOverlayRenderer(LightOverlayTicker ticker) {
+        this.ticker = ticker;
+    }
+    
+    @Override
+    public void accept(PoseStack poses) {
+        if (LightOverlay.enabled) {
+            LocalPlayer playerEntity = minecraft.player;
+            BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
+            int playerPosX = playerPos.getX() >> 4;
+            int playerPosY = playerPos.getY() >> 5;
+            int playerPosZ = playerPos.getZ() >> 4;
+            CollisionContext collisionContext = CollisionContext.of(playerEntity);
+            Camera camera = minecraft.gameRenderer.getMainCamera();
+            int 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);
+            }
+        }
+    }
+    
+    private void renderLevels(PoseStack poses, Camera camera, BlockPos playerPos, int playerPosX, int playerPosY, int playerPosZ, int chunkRange, CollisionContext collisionContext) {
+        RenderSystem.enableTexture();
+        RenderSystem.depthMask(true);
+        BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
+        BlockPos.MutableBlockPos downMutable = new BlockPos.MutableBlockPos();
+        MultiBufferSource.BufferSource source = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
+        for (Map.Entry<CubicChunkPos, Long2ByteMap> entry : ticker.CHUNK_MAP.entrySet()) {
+            CubicChunkPos 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 (Long2ByteMap.Entry 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) {
+        String text = String.valueOf(level);
+        Font font = minecraft.font;
+        double cameraX = camera.getPosition().x;
+        double cameraY = camera.getPosition().y;
+        VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, collisionContext);
+        if (!upperOutlineShape.isEmpty())
+            cameraY += 1 - upperOutlineShape.max(Direction.Axis.Y);
+        double 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);
+        float size = 0.07F;
+        poses.scale(-size, -size, size);
+        float 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);
+        Tesselator tesselator = Tesselator.getInstance();
+        BufferBuilder builder = tesselator.getBuilder();
+        builder.begin(VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR);
+        BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
+    
+        System.out.println(ticker.CHUNK_MAP.size());
+        for (Map.Entry<CubicChunkPos, Long2ByteMap> entry : ticker.CHUNK_MAP.entrySet()) {
+            CubicChunkPos 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 (Long2ByteMap.Entry objectEntry : entry.getValue().long2ByteEntrySet()) {
+                byte 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)) {
+                        int 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) {
+        double cameraX = camera.getPosition().x;
+        double cameraY = camera.getPosition().y - .005D;
+        double blockOffset = 0;
+        VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, collisionContext);
+        if (!upperOutlineShape.isEmpty()) {
+            blockOffset += upperOutlineShape.max(Direction.Axis.Y);
+        }
+        double cameraZ = camera.getPosition().z;
+        
+        int red = (color >> 16) & 255;
+        int green = (color >> 8) & 255;
+        int blue = color & 255;
+        double x = pos.getX() - cameraX;
+        double y = pos.getY() - cameraY + blockOffset;
+        double 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();
+    }
+    
+    private static final Supplier<MethodHandle> IS_FRUSTUM_VISIBLE = Suppliers.memoize(() -> {
+        try {
+            return MethodHandles.lookup().findStatic(Class.forName("me.shedaniel.lightoverlay." + ArchitecturyTarget.getCurrentTarget() + ".LightOverlayImpl"), "isFrustumVisible",
+                    MethodType.methodType(boolean.class, Frustum.class, double.class, double.class, double.class, double.class, double.class, double.class));
+        } catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    });
+    
+    public boolean isFrustumVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
+        try {
+            return frustum == null || (boolean) IS_FRUSTUM_VISIBLE.get().invokeExact(frustum, minX, minY, minZ, maxX, maxY, maxZ);
+        } catch (Throwable throwable) {
+            throw new RuntimeException(throwable);
+        }
+    }
+}