minecraft-src/net/minecraft/client/renderer/WorldBorderRenderer.java
2025-07-04 03:45:38 +03:00

182 lines
8.3 KiB
Java

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<RenderPass.Draw> 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;
}
}