From: shedaniel Date: Tue, 16 Feb 2021 08:59:41 +0000 (+0800) Subject: 2.8.0 Cubic Chunk calculation, using glList during caching X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=79e1db07c3eff0becc258c39039e361803f18cb4;p=LightOverlay.git 2.8.0 Cubic Chunk calculation, using glList during caching Signed-off-by: shedaniel --- diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/ChunkData.java b/common/src/main/java/me/shedaniel/lightoverlay/common/ChunkData.java new file mode 100644 index 0000000..e4278a1 --- /dev/null +++ b/common/src/main/java/me/shedaniel/lightoverlay/common/ChunkData.java @@ -0,0 +1,131 @@ +package me.shedaniel.lightoverlay.common; + +import it.unimi.dsi.fastutil.longs.Long2ByteMap; +import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.lwjgl.opengl.GL11; + +import java.io.Closeable; + +import static me.shedaniel.lightoverlay.common.LightOverlay.*; + +public class ChunkData implements Closeable { + private static final IllegalStateException WRONG_TYPE = new IllegalStateException("Wrong type accessed!"); + private Long2ByteMap data; + private int glListIndex = 0; + private boolean generatedList = false; + + public ChunkData() { + this(new Long2ByteOpenHashMap()); + } + + public ChunkData(Long2ByteMap data) { + this.data = data; + } + + public Long2ByteMap data() { + return data; + } + + private void compileList(Level level, CollisionContext collisionContext) { + generatedList = true; + + if (data().isEmpty()) { + glListIndex = 0; + return; + } + + glListIndex = GL11.glGenLists(3); + GL11.glNewList(glListIndex, GL11.GL_COMPILE); + GL11.glBegin(GL11.GL_LINES); + color(redColor); + + BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); + for (Long2ByteMap.Entry objectEntry : data().long2ByteEntrySet()) { + byte crossType = objectEntry.getByteValue(); + mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); + if (crossType == CROSS_RED) { + renderCross(level, mutable, collisionContext); + } + } + + GL11.glEnd(); + GL11.glEndList(); + + GL11.glNewList(glListIndex + 1, GL11.GL_COMPILE); + GL11.glBegin(GL11.GL_LINES); + color(yellowColor); + + for (Long2ByteMap.Entry objectEntry : data().long2ByteEntrySet()) { + byte crossType = objectEntry.getByteValue(); + mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); + if (crossType == CROSS_YELLOW) { + renderCross(level, mutable, collisionContext); + } + } + + GL11.glEnd(); + GL11.glEndList(); + + GL11.glNewList(glListIndex + 2, GL11.GL_COMPILE); + GL11.glBegin(GL11.GL_LINES); + color(secondaryColor); + + for (Long2ByteMap.Entry objectEntry : data().long2ByteEntrySet()) { + byte crossType = objectEntry.getByteValue(); + mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); + if (crossType == CROSS_SECONDARY) { + renderCross(level, mutable, collisionContext); + } + } + + GL11.glEnd(); + GL11.glEndList(); + } + + public void renderList(Level level, CollisionContext collisionContext) { + if (!generatedList) { + compileList(level, collisionContext); + } + + if (glListIndex != 0) { + GL11.glCallList(glListIndex); + GL11.glCallList(glListIndex + 1); + GL11.glCallList(glListIndex + 2); + } + } + + private static void color(int color) { + int red = (color >> 16) & 255; + int green = (color >> 8) & 255; + int blue = color & 255; + GL11.glColor4f(red / 255f, green / 255f, blue / 255f, 1f); + } + + public static void renderCross(Level level, BlockPos pos, CollisionContext collisionContext) { + double blockOffset = 0; + VoxelShape upperOutlineShape = level.getBlockState(pos).getShape(level, pos, collisionContext); + if (!upperOutlineShape.isEmpty()) + blockOffset += upperOutlineShape.max(Direction.Axis.Y); + + + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + GL11.glVertex3d(x + .01, y + blockOffset, z + .01); + GL11.glVertex3d(x - .01 + 1, y + blockOffset, z - .01 + 1); + GL11.glVertex3d(x - .01 + 1, y + blockOffset, z + .01); + GL11.glVertex3d(x + .01, y + blockOffset, z - .01 + 1); + } + + @Override + public void close() { + if (glListIndex != 0) { + GL11.glDeleteLists(glListIndex, 3); + } + } +} diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/ClothScreen.java b/common/src/main/java/me/shedaniel/lightoverlay/common/ClothScreen.java index 7175343..1a71dca 100644 --- a/common/src/main/java/me/shedaniel/lightoverlay/common/ClothScreen.java +++ b/common/src/main/java/me/shedaniel/lightoverlay/common/ClothScreen.java @@ -34,6 +34,7 @@ public class ClothScreen { general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.smoothLines"), LightOverlay.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.smoothLines = bool).build()); general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.underwater"), LightOverlay.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.underwater = bool).build()); general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.mushroom"), LightOverlay.mushroom).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.mushroom = bool).build()); + general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.useListWhileCaching"), LightOverlay.useListWhileCaching).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.useListWhileCaching = bool).build()); general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.lineWidth"), Mth.floor(LightOverlay.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> new TextComponent("Light Width: " + LightOverlay.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlay.lineWidth = integer / 100f).build()); general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.yellowColor"), LightOverlay.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlay.yellowColor = color).build()); general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.redColor"), LightOverlay.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlay.redColor = color).build()); diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/CubicChunkPos.java b/common/src/main/java/me/shedaniel/lightoverlay/common/CubicChunkPos.java new file mode 100644 index 0000000..576724e --- /dev/null +++ b/common/src/main/java/me/shedaniel/lightoverlay/common/CubicChunkPos.java @@ -0,0 +1,87 @@ +package me.shedaniel.lightoverlay.common; + +import net.minecraft.core.BlockPos; + +import java.util.Objects; + +public class CubicChunkPos { + public final int x; + public final int y; + public final int z; + + public CubicChunkPos(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public CubicChunkPos(long l) { + this.x = getX(l); + this.y = getY(l); + this.z = getZ(l); + } + + public CubicChunkPos(BlockPos blockPos) { + this.x = blockPos.getX() >> 4; + this.y = blockPos.getY() >> 4; + this.z = blockPos.getZ() >> 4; + } + + public long toLong() { + return asLong(this.x, this.y, this.z); + } + + // Allocate 24 bits to x, 12 bits to y, 24 bits to z + public static long asLong(int x, int y, int z) { + return ((x & 0xffffffL) << 36) | ((y & 0xfffL) << 24) | (z & 0xffffffL); + } + + public static int getX(long l) { + return (int) (l >> 36 & 0xffffffL); + } + + public static int getY(long l) { + return (int) (l >> 24 & 0xfffL); + } + + public static int getZ(long l) { + return (int) (l & 0xffffffL); + } + + public int getMinBlockX() { + return this.x << 4; + } + + public int getMinBlockY() { + return this.y << 4; + } + + public int getMinBlockZ() { + return this.z << 4; + } + + public int getMaxBlockX() { + return (this.x << 4) + 15; + } + + public int getMaxBlockY() { + return (this.y << 4) + 15; + } + + public int getMaxBlockZ() { + return (this.z << 4) + 15; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CubicChunkPos that = (CubicChunkPos) o; + return x == that.x && y == that.y && z == that.z; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, z); + } +} diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlay.java b/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlay.java index 28ee75d..d3eb131 100644 --- a/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlay.java +++ b/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlay.java @@ -6,8 +6,7 @@ import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.math.Transformation; -import it.unimi.dsi.fastutil.longs.Long2ReferenceMap; -import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ByteMap; import me.shedaniel.architectury.event.events.GuiEvent; import me.shedaniel.architectury.event.events.client.ClientTickEvent; import me.shedaniel.architectury.platform.Platform; @@ -30,7 +29,6 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.biome.Biome; @@ -70,10 +68,11 @@ public class LightOverlay { public static boolean smoothLines = true; public static boolean underwater = false; public static boolean mushroom = false; + public static boolean useListWhileCaching = true; public static float lineWidth = 1.0F; public static int yellowColor = 0xFFFF00, redColor = 0xFF0000, secondaryColor = 0x0000FF; public static File configFile; - + private static KeyMapping enableOverlay; private static boolean enabled = false; private static final LazyLoadedValue> TESTING_ENTITY_TYPE = new LazyLoadedValue<>(() -> @@ -85,9 +84,9 @@ public class LightOverlay { thread.setDaemon(true); return thread; }); - private static final Set POS = Collections.synchronizedSet(new HashSet<>()); - private static final Set CALCULATING_POS = Collections.synchronizedSet(new HashSet<>()); - private static final Map> CHUNK_MAP = Maps.newConcurrentMap(); + private static final Set POS = Collections.synchronizedSet(new HashSet<>()); + private static final Set CALCULATING_POS = Collections.synchronizedSet(new HashSet<>()); + private static final Map CHUNK_MAP = Maps.newConcurrentMap(); private static final Minecraft CLIENT = Minecraft.getInstance(); private static long ticks = 0; @@ -114,56 +113,67 @@ public class LightOverlay { RenderSystem.depthMask(true); BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); BlockPos.MutableBlockPos downMutable = new BlockPos.MutableBlockPos(); - for (Map.Entry> entry : CHUNK_MAP.entrySet()) { + for (Map.Entry entry : CHUNK_MAP.entrySet()) { if (caching && (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { continue; } - for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { - if (objectEntry.getValue() instanceof Byte) { - mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); - if (mutable.closerThan(playerPos, reach)) { - if (frustum == null || isFrustumVisible(frustum, 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(CLIENT, camera, world, mutable, downMutable, (Byte) objectEntry.getValue(), collisionContext); - } + for (Long2ByteMap.Entry objectEntry : entry.getValue().data().long2ByteEntrySet()) { + mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); + if (mutable.closerThan(playerPos, reach)) { + if (frustum == null || isFrustumVisible(frustum, 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(CLIENT, camera, world, mutable, downMutable, objectEntry.getByteValue(), collisionContext); } } } } RenderSystem.enableDepthTest(); } else { + boolean useList = useListWhileCaching && caching; RenderSystem.enableDepthTest(); RenderSystem.disableTexture(); RenderSystem.enableBlend(); + RenderSystem.enableCull(); RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH); GL11.glLineWidth(lineWidth); - GL11.glBegin(GL11.GL_LINES); + if (!useList) GL11.glBegin(GL11.GL_LINES); + BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); - for (Map.Entry> entry : CHUNK_MAP.entrySet()) { - if (caching && (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { + + if (useList) GL11.glTranslated(-camera.getPosition().x, -camera.getPosition().y + .01D, -camera.getPosition().z); + for (Map.Entry entry : CHUNK_MAP.entrySet()) { + CubicChunkPos chunkPos = entry.getKey(); + if (caching && (Mth.abs(chunkPos.x - playerPosX) > getChunkRange() || Mth.abs(chunkPos.z - playerPosZ) > getChunkRange())) { continue; } - for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { - if (objectEntry.getValue() instanceof CrossType) { + + if (useList) { + if (frustum == null || isFrustumVisible(frustum, chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) { + entry.getValue().renderList(world, collisionContext); + } + } else { + for (Long2ByteMap.Entry objectEntry : entry.getValue().data().long2ByteEntrySet()) { + byte crossType = objectEntry.getByteValue(); mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); if (mutable.closerThan(playerPos, reach)) { if (frustum == null || isFrustumVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { - int color = objectEntry.getValue() == CrossType.RED ? redColor : objectEntry.getValue() == CrossType.YELLOW ? yellowColor : secondaryColor; + int color = crossType == CROSS_RED ? redColor : crossType == CROSS_YELLOW ? yellowColor : secondaryColor; renderCross(camera, world, mutable, color, collisionContext); } } } } } - GL11.glEnd(); + + if (!useList) GL11.glEnd(); RenderSystem.disableBlend(); RenderSystem.enableTexture(); if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH); } } }); - + GuiEvent.DEBUG_TEXT_LEFT.register(list -> { if (enabled) { if (caching) { @@ -171,16 +181,16 @@ public class LightOverlay { } else { list.add("[Light Overlay] Enabled"); } - }else { + } else { list.add("[Light Overlay] Disabled"); } }); ClientTickEvent.CLIENT_POST.register(LightOverlay::tick); } - private static void processChunk(ChunkPos pos, int playerPosX, int playerPosZ, CollisionContext context) { + private static void processChunk(CubicChunkPos pos, int playerPosX, int playerPosY, int playerPosZ, CollisionContext context) { CALCULATING_POS.remove(pos); - if (Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange() || POS.contains(pos)) { + if (Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.y - playerPosY) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange() || POS.contains(pos)) { return; } try { @@ -190,15 +200,17 @@ public class LightOverlay { } } - public static void queueChunkAndNear(ChunkPos pos) { + public static void queueChunkAndNear(CubicChunkPos pos) { for (int xOffset = -1; xOffset <= 1; xOffset++) { - for (int zOffset = -1; zOffset <= 1; zOffset++) { - queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset)); + for (int yOffset = -1; yOffset <= 1; yOffset++) { + for (int zOffset = -1; zOffset <= 1; zOffset++) { + queueChunk(new CubicChunkPos(pos.x + xOffset, pos.y + yOffset, pos.z + zOffset)); + } } } } - public static void queueChunk(ChunkPos pos) { + public static void queueChunk(CubicChunkPos pos) { if (enabled && caching && !CALCULATING_POS.contains(pos)) { POS.add(pos); } @@ -208,61 +220,65 @@ public class LightOverlay { return Math.max(Mth.ceil(reach / 16f), 1); } - private static void calculateChunk(LevelChunk chunk, Level world, ChunkPos chunkPos, CollisionContext entityContext) { + private static void calculateChunk(LevelChunk chunk, Level world, CubicChunkPos chunkPos, CollisionContext collisionContext) { if (world != null && chunk != null) { - Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); + ChunkData chunkData = new ChunkData(); LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK); LayerLightEventListener sky = showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY); - for (BlockPos pos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), 0, chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), 256, chunkPos.getMaxBlockZ())) { + for (BlockPos pos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) { BlockPos down = pos.below(); if (showNumber) { - int level = getCrossLevel(pos, down, chunk, block, entityContext); + int level = getCrossLevel(pos, down, chunk, block, collisionContext); if (level >= 0) { - map.put(pos.asLong(), Byte.valueOf((byte) level)); + chunkData.data().put(pos.asLong(), (byte) level); } } else { - Biome biome = mushroom ? world.getBiome(pos) : null; - CrossType type = getCrossType(pos, biome,down, chunk, block, sky, entityContext); - if (type != CrossType.NONE) { - map.put(pos.asLong(), type); + Biome biome = !mushroom ? world.getBiome(pos) : null; + byte type = getCrossType(pos, biome, down, chunk, block, sky, collisionContext); + if (type != CROSS_NONE) { + chunkData.data().put(pos.asLong(), type); } } } - CHUNK_MAP.put(chunkPos, map); + CHUNK_MAP.put(chunkPos, chunkData); } else { - CHUNK_MAP.remove(chunkPos); + ChunkData data = CHUNK_MAP.remove(chunkPos); + + if (data != null) { + data.close(); + } } } - public static CrossType getCrossType(BlockPos pos, Biome biome, BlockPos down, BlockGetter world, LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) { + public static byte getCrossType(BlockPos pos, Biome biome, BlockPos down, BlockGetter world, LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) { BlockState blockBelowState = world.getBlockState(down); BlockState blockUpperState = world.getBlockState(pos); VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext); if (!underwater && !blockUpperState.getFluidState().isEmpty()) - return CrossType.NONE; + return CROSS_NONE; // Check if the outline is full if (Block.isFaceFull(upperCollisionShape, Direction.UP)) - return CrossType.NONE; + return CROSS_NONE; // TODO: Not to hard code no redstone if (blockUpperState.isSignalSource()) - return CrossType.NONE; + return CROSS_NONE; // Check if the collision has a bump if (upperCollisionShape.max(Direction.Axis.Y) > 0) - return CrossType.NONE; + return CROSS_NONE; if (blockUpperState.getBlock().is(BlockTags.RAILS)) - return CrossType.NONE; + return CROSS_NONE; // Check block state allow spawning (excludes bedrock and barriers automatically) if (!blockBelowState.isValidSpawn(world, down, TESTING_ENTITY_TYPE.get())) - return CrossType.NONE; + return CROSS_NONE; if (!mushroom && Biome.BiomeCategory.MUSHROOM == biome.getBiomeCategory()) - return CrossType.NONE; + return CROSS_NONE; int blockLightLevel = block.getLightValue(pos); int skyLightLevel = sky.getLightValue(pos); if (blockLightLevel > higherCrossLevel) - return CrossType.NONE; + return CROSS_NONE; if (skyLightLevel > higherCrossLevel) - return CrossType.YELLOW; - return lowerCrossLevel >= 0 && blockLightLevel > lowerCrossLevel ? CrossType.SECONDARY : CrossType.RED; + return CROSS_YELLOW; + return lowerCrossLevel >= 0 && blockLightLevel > lowerCrossLevel ? CROSS_SECONDARY : CROSS_RED; } public static int getCrossLevel(BlockPos pos, BlockPos down, BlockGetter world, LayerLightEventListener view, CollisionContext collisionContext) { @@ -282,12 +298,13 @@ public class LightOverlay { } public static void renderCross(Camera camera, Level world, BlockPos pos, int color, CollisionContext collisionContext) { - double d0 = camera.getPosition().x; - double d1 = camera.getPosition().y - .005D; + 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()) - d1 -= upperOutlineShape.max(Direction.Axis.Y); - double d2 = camera.getPosition().z; + blockOffset += upperOutlineShape.max(Direction.Axis.Y); + double cameraZ = camera.getPosition().z; int red = (color >> 16) & 255; int green = (color >> 8) & 255; @@ -296,33 +313,33 @@ public class LightOverlay { int y = pos.getY(); int z = pos.getZ(); RenderSystem.color4f(red / 255f, green / 255f, blue / 255f, 1f); - GL11.glVertex3d(x + .01 - d0, y - d1, z + .01 - d2); - GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z - .01 + 1 - d2); - GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z + .01 - d2); - GL11.glVertex3d(x + .01 - d0, y - d1, z - .01 + 1 - d2); + GL11.glVertex3d(x + .01 - cameraX, y - cameraY + blockOffset, z + .01 - cameraZ); + GL11.glVertex3d(x - .01 + 1 - cameraX, y - cameraY + blockOffset, z - .01 + 1 - cameraZ); + GL11.glVertex3d(x - .01 + 1 - cameraX, y - cameraY + blockOffset, z + .01 - cameraZ); + GL11.glVertex3d(x + .01 - cameraX, y - cameraY + blockOffset, z - .01 + 1 - cameraZ); } @SuppressWarnings("deprecation") - public static void renderLevel(Minecraft client, Camera camera, Level world, BlockPos pos, BlockPos down, int level, CollisionContext collisionContext) { + public static void renderLevel(Minecraft client, Camera camera, Level world, BlockPos pos, BlockPos down, byte level, CollisionContext collisionContext) { String text = String.valueOf(level); - Font textRenderer_1 = client.font; - double double_4 = camera.getPosition().x; - double double_5 = camera.getPosition().y; + Font font = client.font; + double cameraX = camera.getPosition().x; + double cameraY = camera.getPosition().y; VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, collisionContext); if (!upperOutlineShape.isEmpty()) - double_5 += 1 - upperOutlineShape.max(Direction.Axis.Y); - double double_6 = camera.getPosition().z; + cameraY += 1 - upperOutlineShape.max(Direction.Axis.Y); + double cameraZ = camera.getPosition().z; RenderSystem.pushMatrix(); - RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6)); + RenderSystem.translatef((float) (pos.getX() + 0.5f - cameraX), (float) (pos.getY() - cameraY) + 0.005f, (float) (pos.getZ() + 0.5f - cameraZ)); RenderSystem.rotatef(90, 1, 0, 0); RenderSystem.normal3f(0.0F, 1.0F, 0.0F); float size = 0.07F; RenderSystem.scalef(-size, -size, size); - float float_3 = (float) (-textRenderer_1.width(text)) / 2.0F + 0.4f; + float float_3 = (float) (-font.width(text)) / 2.0F + 0.4f; RenderSystem.enableAlphaTest(); - MultiBufferSource.BufferSource immediate = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); - textRenderer_1.drawInBatch(text, float_3, -3.5f, level > higherCrossLevel ? 0xff042404 : (lowerCrossLevel >= 0 && level > lowerCrossLevel ? 0xff0066ff : 0xff731111), false, Transformation.identity().getMatrix(), immediate, false, 0, 15728880); - immediate.endBatch(); + MultiBufferSource.BufferSource source = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); + font.drawInBatch(text, float_3, -3.5f, level > higherCrossLevel ? 0xff042404 : (lowerCrossLevel >= 0 && level > lowerCrossLevel ? 0xff0066ff : 0xff731111), false, Transformation.identity().getMatrix(), source, false, 0, 15728880); + source.endBatch(); RenderSystem.popMatrix(); } @@ -344,6 +361,8 @@ public class LightOverlay { showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true"); smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true"); underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true"); + mushroom = ((String) properties.computeIfAbsent("mushroom", a -> "false")).equalsIgnoreCase("true"); + useListWhileCaching = ((String) properties.computeIfAbsent("useListWhileCaching", a -> "true")).equalsIgnoreCase("true"); lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1")); { int r, g, b; @@ -380,6 +399,8 @@ public class LightOverlay { showNumber = false; smoothLines = true; underwater = false; + mushroom = false; + useListWhileCaching = true; try { saveConfig(file); } catch (IOException ex) { @@ -389,6 +410,9 @@ public class LightOverlay { if (secondaryLevel >= crossLevel) System.err.println("[Light Overlay] Secondary Level is higher than Cross Level"); lowerCrossLevel = Math.min(crossLevel, secondaryLevel); higherCrossLevel = Math.max(crossLevel, secondaryLevel); + for (ChunkData data : CHUNK_MAP.values()) { + data.close(); + } CHUNK_MAP.clear(); POS.clear(); } @@ -411,6 +435,10 @@ public class LightOverlay { fos.write("\n".getBytes()); fos.write(("underwater=" + underwater).getBytes()); fos.write("\n".getBytes()); + fos.write(("mushroom=" + mushroom).getBytes()); + fos.write("\n".getBytes()); + fos.write(("useListWhileCaching=" + useListWhileCaching).getBytes()); + fos.write("\n".getBytes()); fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes()); fos.write("\n".getBytes()); fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes()); @@ -465,22 +493,28 @@ public class LightOverlay { private static void tick(Minecraft minecraft) { while (enableOverlay.consumeClick()) enabled = !enabled; - + try { ticks++; if (CLIENT.player == null || !enabled) { POS.clear(); CALCULATING_POS.clear(); EXECUTOR.getQueue().clear(); + for (ChunkData data : CHUNK_MAP.values()) { + data.close(); + } CHUNK_MAP.clear(); } else { LocalPlayer player = CLIENT.player; ClientLevel world = CLIENT.level; CollisionContext collisionContext = CollisionContext.of(player); - + if (!caching) { CALCULATING_POS.clear(); POS.clear(); + for (ChunkData data : CHUNK_MAP.values()) { + data.close(); + } CHUNK_MAP.clear(); BlockPos playerPos = player.blockPosition(); LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK); @@ -488,50 +522,54 @@ public class LightOverlay { BlockPos.MutableBlockPos downPos = new BlockPos.MutableBlockPos(); Iterable iterate = BlockPos.betweenClosed(playerPos.getX() - reach, playerPos.getY() - reach, playerPos.getZ() - reach, playerPos.getX() + reach, playerPos.getY() + reach, playerPos.getZ() + reach); - Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); - CHUNK_MAP.put(new ChunkPos(0, 0), map); + ChunkData chunkData = new ChunkData(); + CHUNK_MAP.put(new CubicChunkPos(0, 0, 0), chunkData); for (BlockPos blockPos : iterate) { downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()); if (showNumber) { int level = getCrossLevel(blockPos, downPos, world, block, collisionContext); if (level >= 0) { - map.put(blockPos.asLong(), Byte.valueOf((byte) level)); + chunkData.data().put(blockPos.asLong(), (byte) level); } } else { - Biome biome = mushroom ? world.getBiome(blockPos) : null; - CrossType type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext); - if (type != CrossType.NONE) { - map.put(blockPos.asLong(), type); + Biome biome = !mushroom ? world.getBiome(blockPos) : null; + byte type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext); + if (type != CROSS_NONE) { + chunkData.data().put(blockPos.asLong(), type); } } } } else { int playerPosX = ((int) player.getX()) >> 4; + int playerPosY = ((int) player.getY()) >> 4; int playerPosZ = ((int) player.getZ()) >> 4; for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) { - for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) { - if (Mth.abs(chunkX - playerPosX) > getChunkRange() || Mth.abs(chunkZ - playerPosZ) > getChunkRange()) - continue; - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - if (!CHUNK_MAP.containsKey(chunkPos)) - queueChunk(chunkPos); + for (int chunkY = Math.max(playerPosY - getChunkRange(), 0); chunkY <= playerPosY + getChunkRange() && chunkY <= 15; chunkY++) { + for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) { + if (Mth.abs(chunkX - playerPosX) > getChunkRange() || Mth.abs(chunkY - playerPosY) > getChunkRange() || Mth.abs(chunkZ - playerPosZ) > getChunkRange()) + continue; + CubicChunkPos chunkPos = new CubicChunkPos(chunkX, chunkY, chunkZ); + if (!CHUNK_MAP.containsKey(chunkPos)) + queueChunk(chunkPos); + } } } for (int 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; - ChunkPos c1 = null, c2 = null, c3 = null; + CubicChunkPos c1 = null, c2 = null, c3 = null; synchronized (POS) { - Iterator iterator = POS.iterator(); + Iterator iterator = POS.iterator(); while (iterator.hasNext()) { - ChunkPos pos = iterator.next(); - if (Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange() || CALCULATING_POS.contains(pos)) { + CubicChunkPos pos = iterator.next(); + if (Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.y - playerPosY) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange() || CALCULATING_POS.contains(pos)) { iterator.remove(); } else { - if (isFrustumVisible(frustum, pos.getMinBlockX(), 0, pos.getMinBlockZ(), pos.getMaxBlockX(), 256, pos.getMaxBlockZ())) { + if (isFrustumVisible(frustum, pos.getMinBlockX(), pos.getMinBlockY(), pos.getMinBlockZ(), pos.getMaxBlockX(), pos.getMaxBlockY(), pos.getMaxBlockZ())) { int i = Math.abs(pos.x - playerPosX); - int j = Math.abs(pos.z - playerPosZ); - double distance = Math.sqrt(i * i + j * j); + int j = Math.abs(pos.y - playerPosY); + int k = Math.abs(pos.z - playerPosZ); + double distance = Math.sqrt(i * i + j * j + k * k); if (distance < d1) { d3 = d2; d2 = d1; @@ -555,9 +593,9 @@ public class LightOverlay { } } } - ChunkPos finalC1 = c1; - ChunkPos finalC2 = c2; - ChunkPos finalC3 = c3; + CubicChunkPos finalC1 = c1; + CubicChunkPos finalC2 = c2; + CubicChunkPos finalC3 = c3; if (finalC1 != null) { CALCULATING_POS.add(finalC1); if (finalC2 != null) { @@ -568,15 +606,23 @@ public class LightOverlay { } EXECUTOR.submit(() -> { int playerPosX1 = ((int) CLIENT.player.getX()) >> 4; + int playerPosY1 = ((int) CLIENT.player.getY()) >> 4; int playerPosZ1 = ((int) CLIENT.player.getZ()) >> 4; - if (finalC1 != null) processChunk(finalC1, playerPosX1, playerPosZ1, collisionContext); - if (finalC2 != null) processChunk(finalC2, playerPosX1, playerPosZ1, collisionContext); - if (finalC3 != null) processChunk(finalC3, playerPosX1, playerPosZ1, collisionContext); + if (finalC1 != null) processChunk(finalC1, playerPosX1, playerPosY1, playerPosZ1, collisionContext); + if (finalC2 != null) processChunk(finalC2, playerPosX1, playerPosY1, playerPosZ1, collisionContext); + if (finalC3 != null) processChunk(finalC3, playerPosX1, playerPosY1, playerPosZ1, collisionContext); }); } } if (ticks % 50 == 0) { - CHUNK_MAP.entrySet().removeIf(pos -> Mth.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || Mth.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2); + Iterator> iterator = CHUNK_MAP.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() * 2 || Mth.abs(entry.getKey().y - playerPosY) > getChunkRange() * 2 || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange() * 2) { + entry.getValue().close(); + iterator.remove(); + } + } } } } @@ -585,10 +631,8 @@ public class LightOverlay { } } - private enum CrossType { - YELLOW, - RED, - SECONDARY, - NONE - } + public static final byte CROSS_YELLOW = 0; + public static final byte CROSS_RED = 1; + public static final byte CROSS_SECONDARY = 2; + public static final byte CROSS_NONE = 2; } diff --git a/common/src/main/resources/assets/lightoverlay/lang/en_us.json b/common/src/main/resources/assets/lightoverlay/lang/en_us.json index 201186b..d07f5a6 100755 --- a/common/src/main/resources/assets/lightoverlay/lang/en_us.json +++ b/common/src/main/resources/assets/lightoverlay/lang/en_us.json @@ -15,5 +15,6 @@ "config.lightoverlay.secondaryColor": "Secondary Color:", "config.lightoverlay.redColor": "Red Color:", "config.lightoverlay.invalidColor": "Invalid Color", - "config.lightoverlay.mushroom": "Display in Mushroom Biomes:" + "config.lightoverlay.mushroom": "Display in Mushroom Biomes:", + "config.lightoverlay.useListWhileCaching": "Use glList while caching:" } \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle index 4a3be61..b8a902a 100755 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -7,6 +7,7 @@ minecraft { } architectury { + transformerVersion = "2.0.9999" platformSetupLoomIde() fabric() } @@ -22,6 +23,10 @@ configurations { shadow } +repositories { + mavenLocal() +} + dependencies { minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" mappings minecraft.officialMojangMappings() diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java index 5320e78..ac553e6 100644 --- a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java +++ b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java @@ -1,5 +1,6 @@ package me.shedaniel.lightoverlay.fabric.mixin; +import me.shedaniel.lightoverlay.common.CubicChunkPos; import me.shedaniel.lightoverlay.common.LightOverlay; import net.minecraft.network.Connection; import net.minecraft.network.PacketListener; @@ -8,7 +9,6 @@ import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; -import net.minecraft.world.level.ChunkPos; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -19,13 +19,19 @@ public class MixinClientConnection { @Inject(method = "genericsFtw", at = @At("HEAD")) private static void handlePacket(Packet packet, PacketListener listener, CallbackInfo ci) { if (packet instanceof ClientboundBlockUpdatePacket) { - LightOverlay.queueChunkAndNear(new ChunkPos(((ClientboundBlockUpdatePacket) packet).getPos())); + LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundBlockUpdatePacket) packet).getPos())); } else if (packet instanceof ClientboundSetChunkCacheCenterPacket) { - LightOverlay.queueChunkAndNear(new ChunkPos(((ClientboundSetChunkCacheCenterPacket) packet).getX(), ((ClientboundSetChunkCacheCenterPacket) packet).getZ())); + for (int y = 0; y <= 15; y++) { + LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundSetChunkCacheCenterPacket) packet).getX(), y, ((ClientboundSetChunkCacheCenterPacket) packet).getZ())); + } } else if (packet instanceof ClientboundSectionBlocksUpdatePacket) { - LightOverlay.queueChunkAndNear(new ChunkPos(((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getX(), ((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getZ())); + for (int y = 0; y <= 15; y++) { + LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getX(), y, ((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getZ())); + } } else if (packet instanceof ClientboundLightUpdatePacket) { - LightOverlay.queueChunk(new ChunkPos(((ClientboundLightUpdatePacket) packet).getX(), ((ClientboundLightUpdatePacket) packet).getZ())); + for (int y = 0; y <= 15; y++) { + LightOverlay.queueChunk(new CubicChunkPos(((ClientboundLightUpdatePacket) packet).getX(), y, ((ClientboundLightUpdatePacket) packet).getZ())); + } } } } diff --git a/forge/build.gradle b/forge/build.gradle index 36ed12a..95ece2e 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -7,6 +7,7 @@ configurations { } architectury { + transformerVersion = "2.0.9999" platformSetupLoomIde() forge() } @@ -15,11 +16,6 @@ loom { mixinConfig "lightoverlay-forge.mixins.json" } -repositories { - jcenter() - maven { url "https://dl.bintray.com/shedaniel/cloth-config-2" } -} - processResources { filesMatching("META-INF/mods.toml") { expand "version": project.version @@ -27,6 +23,10 @@ processResources { inputs.property "META-INF/mods.toml", project.version } +repositories { + mavenLocal() +} + dependencies { minecraft("com.mojang:minecraft:${rootProject.architectury.minecraft}") mappings(minecraft.officialMojangMappings()) diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/mixin/MixinClientConnection.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/mixin/MixinClientConnection.java index b6c8274..02f46de 100644 --- a/forge/src/main/java/me/shedaniel/lightoverlay/forge/mixin/MixinClientConnection.java +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/mixin/MixinClientConnection.java @@ -1,5 +1,6 @@ package me.shedaniel.lightoverlay.forge.mixin; +import me.shedaniel.lightoverlay.common.CubicChunkPos; import me.shedaniel.lightoverlay.common.LightOverlay; import net.minecraft.network.Connection; import net.minecraft.network.PacketListener; @@ -8,7 +9,6 @@ import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; -import net.minecraft.world.level.ChunkPos; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -19,13 +19,19 @@ public class MixinClientConnection { @Inject(method = "genericsFtw", at = @At("HEAD")) private static void handlePacket(Packet packet, PacketListener listener, CallbackInfo ci) { if (packet instanceof ClientboundBlockUpdatePacket) { - LightOverlay.queueChunkAndNear(new ChunkPos(((ClientboundBlockUpdatePacket) packet).getPos())); + LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundBlockUpdatePacket) packet).getPos())); } else if (packet instanceof ClientboundSetChunkCacheCenterPacket) { - LightOverlay.queueChunkAndNear(new ChunkPos(((ClientboundSetChunkCacheCenterPacket) packet).getX(), ((ClientboundSetChunkCacheCenterPacket) packet).getZ())); + for (int y = 0; y <= 15; y++) { + LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundSetChunkCacheCenterPacket) packet).getX(), y, ((ClientboundSetChunkCacheCenterPacket) packet).getZ())); + } } else if (packet instanceof ClientboundSectionBlocksUpdatePacket) { - LightOverlay.queueChunkAndNear(new ChunkPos(((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getX(), ((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getZ())); + for (int y = 0; y <= 15; y++) { + LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getX(), y, ((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getZ())); + } } else if (packet instanceof ClientboundLightUpdatePacket) { - LightOverlay.queueChunk(new ChunkPos(((ClientboundLightUpdatePacket) packet).getX(), ((ClientboundLightUpdatePacket) packet).getZ())); + for (int y = 0; y <= 15; y++) { + LightOverlay.queueChunk(new CubicChunkPos(((ClientboundLightUpdatePacket) packet).getX(), y, ((ClientboundLightUpdatePacket) packet).getZ())); + } } } } diff --git a/gradle.properties b/gradle.properties index ca7eb3d..20a9ef3 100755 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -mod_version=5.7.0 +mod_version=5.8.0 minecraft_version=1.16.4 architectury_version=1.5.112