package net.minecraft.client.renderer; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; 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.GpuTextureView; 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.Collections; 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.world.level.border.WorldBorder; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Vector3f; import org.joml.Vector4f; @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", 40, 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 = ByteBufferBuilder.exactlySized(DefaultVertexFormat.POSITION_TEX.getVertexSize() * 4 * 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.slice(), meshData.vertexBuffer()); } 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; float p = (float)(Util.getMillis() % 3000L) / 3000.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); } TextureManager textureManager = Minecraft.getInstance().getTextureManager(); AbstractTexture abstractTexture = textureManager.getTexture(FORCEFIELD_LOCATION); abstractTexture.setUseMipmaps(false); RenderPipeline renderPipeline = RenderPipelines.WORLD_BORDER; RenderTarget renderTarget = Minecraft.getInstance().getMainRenderTarget(); RenderTarget renderTarget2 = Minecraft.getInstance().levelRenderer.getWeatherTarget(); GpuTextureView gpuTextureView; GpuTextureView gpuTextureView2; if (renderTarget2 != null) { gpuTextureView = renderTarget2.getColorTextureView(); gpuTextureView2 = renderTarget2.getDepthTextureView(); } else { gpuTextureView = renderTarget.getColorTextureView(); gpuTextureView2 = renderTarget.getDepthTextureView(); } GpuBuffer gpuBuffer = this.indices.getBuffer(6); GpuBufferSlice gpuBufferSlice = RenderSystem.getDynamicUniforms() .writeTransform( RenderSystem.getModelViewMatrix(), new Vector4f(m, n, o, (float)h), new Vector3f((float)(this.lastMinX - i), (float)(-cameraPosition.y), (float)(this.lastMinZ - j)), new Matrix4f().translation(p, p, 0.0F), 0.0F ); try (RenderPass renderPass = RenderSystem.getDevice() .createCommandEncoder() .createRenderPass(() -> "World border", gpuTextureView, OptionalInt.empty(), gpuTextureView2, OptionalDouble.empty())) { renderPass.setPipeline(renderPipeline); RenderSystem.bindDefaultUniforms(renderPass); renderPass.setUniform("DynamicTransforms", gpuBufferSlice); renderPass.setIndexBuffer(gpuBuffer, this.indices.type()); renderPass.bindSampler("Sampler0", abstractTexture.getTextureView()); 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, Collections.emptyList(), this); } } } 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; } }