package net.minecraft.client.renderer; import com.mojang.blaze3d.buffers.BufferType; import com.mojang.blaze3d.buffers.BufferUsage; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTexture; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.MeshData; import com.mojang.blaze3d.vertex.VertexFormat; import java.util.ArrayList; import java.util.OptionalDouble; import java.util.OptionalInt; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.AbstractTexture; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.ARGB; import net.minecraft.util.Mth; import net.minecraft.util.TriState; import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; @Environment(EnvType.CLIENT) public class WorldBorderRenderer { public static final ResourceLocation FORCEFIELD_LOCATION = ResourceLocation.withDefaultNamespace("textures/misc/forcefield.png"); private boolean needsRebuild = true; private double lastMinX; private double lastMinZ; private double lastBorderMinX; private double lastBorderMaxX; private double lastBorderMinZ; private double lastBorderMaxZ; private final GpuBuffer worldBorderBuffer = RenderSystem.getDevice() .createBuffer(() -> "World border vertex buffer", BufferType.VERTICES, BufferUsage.DYNAMIC_WRITE, 16 * DefaultVertexFormat.POSITION_TEX.getVertexSize()); private final RenderSystem.AutoStorageIndexBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); private void rebuildWorldBorderBuffer(WorldBorder worldBorder, double renderDistance, double camZ, double camX, float farPlaneDepth, float vBottom, float vTop) { try (ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(DefaultVertexFormat.POSITION_TEX.getVertexSize() * 4)) { double d = worldBorder.getMinX(); double e = worldBorder.getMaxX(); double f = worldBorder.getMinZ(); double g = worldBorder.getMaxZ(); double h = Math.max(Mth.floor(camZ - renderDistance), f); double i = Math.min(Mth.ceil(camZ + renderDistance), g); float j = (Mth.floor(h) & 1) * 0.5F; float k = (float)(i - h) / 2.0F; double l = Math.max(Mth.floor(camX - renderDistance), d); double m = Math.min(Mth.ceil(camX + renderDistance), e); float n = (Mth.floor(l) & 1) * 0.5F; float o = (float)(m - l) / 2.0F; BufferBuilder bufferBuilder = new BufferBuilder(byteBufferBuilder, VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); bufferBuilder.addVertex(0.0F, -farPlaneDepth, (float)(g - h)).setUv(n, vBottom); bufferBuilder.addVertex((float)(m - l), -farPlaneDepth, (float)(g - h)).setUv(o + n, vBottom); bufferBuilder.addVertex((float)(m - l), farPlaneDepth, (float)(g - h)).setUv(o + n, vTop); bufferBuilder.addVertex(0.0F, farPlaneDepth, (float)(g - h)).setUv(n, vTop); bufferBuilder.addVertex(0.0F, -farPlaneDepth, 0.0F).setUv(j, vBottom); bufferBuilder.addVertex(0.0F, -farPlaneDepth, (float)(i - h)).setUv(k + j, vBottom); bufferBuilder.addVertex(0.0F, farPlaneDepth, (float)(i - h)).setUv(k + j, vTop); bufferBuilder.addVertex(0.0F, farPlaneDepth, 0.0F).setUv(j, vTop); bufferBuilder.addVertex((float)(m - l), -farPlaneDepth, 0.0F).setUv(n, vBottom); bufferBuilder.addVertex(0.0F, -farPlaneDepth, 0.0F).setUv(o + n, vBottom); bufferBuilder.addVertex(0.0F, farPlaneDepth, 0.0F).setUv(o + n, vTop); bufferBuilder.addVertex((float)(m - l), farPlaneDepth, 0.0F).setUv(n, vTop); bufferBuilder.addVertex((float)(e - l), -farPlaneDepth, (float)(i - h)).setUv(j, vBottom); bufferBuilder.addVertex((float)(e - l), -farPlaneDepth, 0.0F).setUv(k + j, vBottom); bufferBuilder.addVertex((float)(e - l), farPlaneDepth, 0.0F).setUv(k + j, vTop); bufferBuilder.addVertex((float)(e - l), farPlaneDepth, (float)(i - h)).setUv(j, vTop); try (MeshData meshData = bufferBuilder.buildOrThrow()) { RenderSystem.getDevice().createCommandEncoder().writeToBuffer(this.worldBorderBuffer, meshData.vertexBuffer(), 0); } this.lastBorderMinX = d; this.lastBorderMaxX = e; this.lastBorderMinZ = f; this.lastBorderMaxZ = g; this.lastMinX = l; this.lastMinZ = h; this.needsRebuild = false; } } public void render(WorldBorder worldBorder, Vec3 cameraPosition, double renderDistance, double farPlaneDepth) { double d = worldBorder.getMinX(); double e = worldBorder.getMaxX(); double f = worldBorder.getMinZ(); double g = worldBorder.getMaxZ(); if (( !(cameraPosition.x < e - renderDistance) || !(cameraPosition.x > d + renderDistance) || !(cameraPosition.z < g - renderDistance) || !(cameraPosition.z > f + renderDistance) ) && !(cameraPosition.x < d - renderDistance) && !(cameraPosition.x > e + renderDistance) && !(cameraPosition.z < f - renderDistance) && !(cameraPosition.z > g + renderDistance)) { double h = 1.0 - worldBorder.getDistanceToBorder(cameraPosition.x, cameraPosition.z) / renderDistance; h = Math.pow(h, 4.0); h = Mth.clamp(h, 0.0, 1.0); double i = cameraPosition.x; double j = cameraPosition.z; float k = (float)farPlaneDepth; int l = worldBorder.getStatus().getColor(); float m = ARGB.red(l) / 255.0F; float n = ARGB.green(l) / 255.0F; float o = ARGB.blue(l) / 255.0F; RenderSystem.setShaderColor(m, n, o, (float)h); float p = (float)(Util.getMillis() % 3000L) / 3000.0F; RenderSystem.setTextureMatrix(new Matrix4f().translation(p, p, 0.0F)); float q = (float)(-Mth.frac(cameraPosition.y * 0.5)); float r = q + k; if (this.shouldRebuildWorldBorderBuffer(worldBorder)) { this.rebuildWorldBorderBuffer(worldBorder, renderDistance, j, i, k, r, q); } RenderSystem.setModelOffset((float)(this.lastMinX - i), (float)(-cameraPosition.y), (float)(this.lastMinZ - j)); TextureManager textureManager = Minecraft.getInstance().getTextureManager(); AbstractTexture abstractTexture = textureManager.getTexture(FORCEFIELD_LOCATION); abstractTexture.setFilter(TriState.FALSE, false); RenderPipeline renderPipeline = RenderPipelines.WORLD_BORDER; RenderTarget renderTarget = Minecraft.getInstance().getMainRenderTarget(); RenderTarget renderTarget2 = Minecraft.getInstance().levelRenderer.getWeatherTarget(); GpuTexture gpuTexture; GpuTexture gpuTexture2; if (renderTarget2 != null) { gpuTexture = renderTarget2.getColorTexture(); gpuTexture2 = renderTarget2.getDepthTexture(); } else { gpuTexture = renderTarget.getColorTexture(); gpuTexture2 = renderTarget.getDepthTexture(); } GpuBuffer gpuBuffer = this.indices.getBuffer(6); try (RenderPass renderPass = RenderSystem.getDevice() .createCommandEncoder() .createRenderPass(gpuTexture, OptionalInt.empty(), gpuTexture2, OptionalDouble.empty())) { renderPass.setPipeline(renderPipeline); renderPass.setIndexBuffer(gpuBuffer, this.indices.type()); renderPass.bindSampler("Sampler0", abstractTexture.getTexture()); renderPass.setVertexBuffer(0, this.worldBorderBuffer); ArrayList arrayList = new ArrayList(); for (WorldBorder.DistancePerDirection distancePerDirection : worldBorder.closestBorder(i, j)) { if (distancePerDirection.distance() < renderDistance) { int s = distancePerDirection.direction().get2DDataValue(); arrayList.add(new RenderPass.Draw(0, this.worldBorderBuffer, gpuBuffer, this.indices.type(), 6 * s, 6)); } } renderPass.drawMultipleIndexed(arrayList, null, null); } RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); RenderSystem.resetTextureMatrix(); RenderSystem.resetModelOffset(); } } public void invalidate() { this.needsRebuild = true; } private boolean shouldRebuildWorldBorderBuffer(WorldBorder worldBorder) { return this.needsRebuild || worldBorder.getMinX() != this.lastBorderMinX || worldBorder.getMinZ() != this.lastBorderMinZ || worldBorder.getMaxX() != this.lastBorderMaxX || worldBorder.getMaxZ() != this.lastBorderMaxZ; } }