package net.minecraft.client.renderer.debug; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import java.time.Duration; import java.time.Instant; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.ShapeRenderer; import net.minecraft.core.Direction; import net.minecraft.core.SectionPos; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LayerLightSectionStorage.SectionType; import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape; import net.minecraft.world.phys.shapes.DiscreteVoxelShape; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.joml.Vector4f; @Environment(EnvType.CLIENT) public class LightSectionDebugRenderer implements DebugRenderer.SimpleDebugRenderer { private static final Duration REFRESH_INTERVAL = Duration.ofMillis(500L); private static final int RADIUS = 10; private static final Vector4f LIGHT_AND_BLOCKS_COLOR = new Vector4f(1.0F, 1.0F, 0.0F, 0.25F); private static final Vector4f LIGHT_ONLY_COLOR = new Vector4f(0.25F, 0.125F, 0.0F, 0.125F); private final Minecraft minecraft; private final LightLayer lightLayer; private Instant lastUpdateTime = Instant.now(); @Nullable private LightSectionDebugRenderer.SectionData data; public LightSectionDebugRenderer(Minecraft minecraft, LightLayer lightLayer) { this.minecraft = minecraft; this.lightLayer = lightLayer; } @Override public void render(PoseStack poseStack, MultiBufferSource bufferSource, double camX, double camY, double camZ) { Instant instant = Instant.now(); if (this.data == null || Duration.between(this.lastUpdateTime, instant).compareTo(REFRESH_INTERVAL) > 0) { this.lastUpdateTime = instant; this.data = new LightSectionDebugRenderer.SectionData( this.minecraft.level.getLightEngine(), SectionPos.of(this.minecraft.player.blockPosition()), 10, this.lightLayer ); } renderEdges(poseStack, this.data.lightAndBlocksShape, this.data.minPos, bufferSource, camX, camY, camZ, LIGHT_AND_BLOCKS_COLOR); renderEdges(poseStack, this.data.lightShape, this.data.minPos, bufferSource, camX, camY, camZ, LIGHT_ONLY_COLOR); VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.debugSectionQuads()); renderFaces(poseStack, this.data.lightAndBlocksShape, this.data.minPos, vertexConsumer, camX, camY, camZ, LIGHT_AND_BLOCKS_COLOR); renderFaces(poseStack, this.data.lightShape, this.data.minPos, vertexConsumer, camX, camY, camZ, LIGHT_ONLY_COLOR); } private static void renderFaces( PoseStack poseStack, DiscreteVoxelShape shape, SectionPos pos, VertexConsumer buffer, double camX, double camY, double camZ, Vector4f color ) { shape.forAllFaces((direction, i, j, k) -> { int l = i + pos.getX(); int m = j + pos.getY(); int n = k + pos.getZ(); renderFace(poseStack, buffer, direction, camX, camY, camZ, l, m, n, color); }); } private static void renderEdges( PoseStack poseStack, DiscreteVoxelShape shape, SectionPos pos, MultiBufferSource bufferSource, double camX, double camY, double camZ, Vector4f color ) { shape.forAllEdges((i, j, k, l, m, n) -> { int o = i + pos.getX(); int p = j + pos.getY(); int q = k + pos.getZ(); int r = l + pos.getX(); int s = m + pos.getY(); int t = n + pos.getZ(); VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.debugLineStrip(1.0)); renderEdge(poseStack, vertexConsumer, camX, camY, camZ, o, p, q, r, s, t, color); }, true); } private static void renderFace( PoseStack poseStack, VertexConsumer buffer, Direction face, double camX, double camY, double camZ, int blockX, int blockY, int blockZ, Vector4f color ) { float f = (float)(SectionPos.sectionToBlockCoord(blockX) - camX); float g = (float)(SectionPos.sectionToBlockCoord(blockY) - camY); float h = (float)(SectionPos.sectionToBlockCoord(blockZ) - camZ); ShapeRenderer.renderFace(poseStack, buffer, face, f, g, h, f + 16.0F, g + 16.0F, h + 16.0F, color.x(), color.y(), color.z(), color.w()); } private static void renderEdge( PoseStack poseStack, VertexConsumer buffer, double camX, double camY, double camZ, int x1, int y1, int z1, int x2, int y2, int z2, Vector4f color ) { float f = (float)(SectionPos.sectionToBlockCoord(x1) - camX); float g = (float)(SectionPos.sectionToBlockCoord(y1) - camY); float h = (float)(SectionPos.sectionToBlockCoord(z1) - camZ); float i = (float)(SectionPos.sectionToBlockCoord(x2) - camX); float j = (float)(SectionPos.sectionToBlockCoord(y2) - camY); float k = (float)(SectionPos.sectionToBlockCoord(z2) - camZ); Matrix4f matrix4f = poseStack.last().pose(); buffer.addVertex(matrix4f, f, g, h).setColor(color.x(), color.y(), color.z(), 1.0F); buffer.addVertex(matrix4f, i, j, k).setColor(color.x(), color.y(), color.z(), 1.0F); } @Environment(EnvType.CLIENT) static final class SectionData { final DiscreteVoxelShape lightAndBlocksShape; final DiscreteVoxelShape lightShape; final SectionPos minPos; SectionData(LevelLightEngine levelLightEngine, SectionPos pos, int radius, LightLayer lightLayer) { int i = radius * 2 + 1; this.lightAndBlocksShape = new BitSetDiscreteVoxelShape(i, i, i); this.lightShape = new BitSetDiscreteVoxelShape(i, i, i); for (int j = 0; j < i; j++) { for (int k = 0; k < i; k++) { for (int l = 0; l < i; l++) { SectionPos sectionPos = SectionPos.of(pos.x() + l - radius, pos.y() + k - radius, pos.z() + j - radius); SectionType sectionType = levelLightEngine.getDebugSectionType(lightLayer, sectionPos); if (sectionType == SectionType.LIGHT_AND_DATA) { this.lightAndBlocksShape.fill(l, k, j); this.lightShape.fill(l, k, j); } else if (sectionType == SectionType.LIGHT_ONLY) { this.lightShape.fill(l, k, j); } } } } this.minPos = SectionPos.of(pos.x() - radius, pos.y() - radius, pos.z() - radius); } } }