minecraft-src/net/minecraft/client/renderer/CloudRenderer.java
2025-07-04 03:15:13 +03:00

374 lines
13 KiB
Java

package net.minecraft.client.renderer;
import com.mojang.blaze3d.buffers.BufferUsage;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.CloudStatus;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class CloudRenderer extends SimplePreparableReloadListener<Optional<CloudRenderer.TextureData>> implements AutoCloseable {
private static final Logger LOGGER = LogUtils.getLogger();
private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.withDefaultNamespace("textures/environment/clouds.png");
private static final float CELL_SIZE_IN_BLOCKS = 12.0F;
private static final float HEIGHT_IN_BLOCKS = 4.0F;
private static final float BLOCKS_PER_SECOND = 0.6F;
private static final long EMPTY_CELL = 0L;
private static final int COLOR_OFFSET = 4;
private static final int NORTH_OFFSET = 3;
private static final int EAST_OFFSET = 2;
private static final int SOUTH_OFFSET = 1;
private static final int WEST_OFFSET = 0;
private boolean needsRebuild = true;
private int prevCellX = Integer.MIN_VALUE;
private int prevCellZ = Integer.MIN_VALUE;
private CloudRenderer.RelativeCameraPos prevRelativeCameraPos = CloudRenderer.RelativeCameraPos.INSIDE_CLOUDS;
@Nullable
private CloudStatus prevType;
@Nullable
private CloudRenderer.TextureData texture;
private final VertexBuffer vertexBuffer = new VertexBuffer(BufferUsage.STATIC_WRITE);
private boolean vertexBufferEmpty;
protected Optional<CloudRenderer.TextureData> prepare(ResourceManager resourceManager, ProfilerFiller profilerFiller) {
try {
InputStream inputStream = resourceManager.open(TEXTURE_LOCATION);
Optional var20;
try (NativeImage nativeImage = NativeImage.read(inputStream)) {
int i = nativeImage.getWidth();
int j = nativeImage.getHeight();
long[] ls = new long[i * j];
for (int k = 0; k < j; k++) {
for (int l = 0; l < i; l++) {
int m = nativeImage.getPixel(l, k);
if (isCellEmpty(m)) {
ls[l + k * i] = 0L;
} else {
boolean bl = isCellEmpty(nativeImage.getPixel(l, Math.floorMod(k - 1, j)));
boolean bl2 = isCellEmpty(nativeImage.getPixel(Math.floorMod(l + 1, j), k));
boolean bl3 = isCellEmpty(nativeImage.getPixel(l, Math.floorMod(k + 1, j)));
boolean bl4 = isCellEmpty(nativeImage.getPixel(Math.floorMod(l - 1, j), k));
ls[l + k * i] = packCellData(m, bl, bl2, bl3, bl4);
}
}
}
var20 = Optional.of(new CloudRenderer.TextureData(ls, i, j));
} catch (Throwable var18) {
if (inputStream != null) {
try {
inputStream.close();
} catch (Throwable var15) {
var18.addSuppressed(var15);
}
}
throw var18;
}
if (inputStream != null) {
inputStream.close();
}
return var20;
} catch (IOException var19) {
LOGGER.error("Failed to load cloud texture", (Throwable)var19);
return Optional.empty();
}
}
protected void apply(Optional<CloudRenderer.TextureData> optional, ResourceManager resourceManager, ProfilerFiller profilerFiller) {
this.texture = (CloudRenderer.TextureData)optional.orElse(null);
this.needsRebuild = true;
}
private static boolean isCellEmpty(int color) {
return ARGB.alpha(color) < 10;
}
private static long packCellData(int color, boolean northEmpty, boolean eastEmpty, boolean southEmpty, boolean westEmpty) {
return (long)color << 4 | (northEmpty ? 1 : 0) << 3 | (eastEmpty ? 1 : 0) << 2 | (southEmpty ? 1 : 0) << 1 | (westEmpty ? 1 : 0) << 0;
}
private static int getColor(long cellData) {
return (int)(cellData >> 4 & 4294967295L);
}
private static boolean isNorthEmpty(long cellData) {
return (cellData >> 3 & 1L) != 0L;
}
private static boolean isEastEmpty(long cellData) {
return (cellData >> 2 & 1L) != 0L;
}
private static boolean isSouthEmpty(long cellData) {
return (cellData >> 1 & 1L) != 0L;
}
private static boolean isWestEmpty(long cellData) {
return (cellData >> 0 & 1L) != 0L;
}
public void render(int height, CloudStatus cloudStatus, float color, Matrix4f frustumMatrix, Matrix4f projectionMatrix, Vec3 cameraPosiiton, float ticks) {
if (this.texture != null) {
float f = (float)(color - cameraPosiiton.y);
float g = f + 4.0F;
CloudRenderer.RelativeCameraPos relativeCameraPos;
if (g < 0.0F) {
relativeCameraPos = CloudRenderer.RelativeCameraPos.ABOVE_CLOUDS;
} else if (f > 0.0F) {
relativeCameraPos = CloudRenderer.RelativeCameraPos.BELOW_CLOUDS;
} else {
relativeCameraPos = CloudRenderer.RelativeCameraPos.INSIDE_CLOUDS;
}
double d = cameraPosiiton.x + ticks * 0.030000001F;
double e = cameraPosiiton.z + 3.96F;
double h = this.texture.width * 12.0;
double i = this.texture.height * 12.0;
d -= Mth.floor(d / h) * h;
e -= Mth.floor(e / i) * i;
int j = Mth.floor(d / 12.0);
int k = Mth.floor(e / 12.0);
float l = (float)(d - j * 12.0F);
float m = (float)(e - k * 12.0F);
RenderType renderType = cloudStatus == CloudStatus.FANCY ? RenderType.clouds() : RenderType.flatClouds();
this.vertexBuffer.bind();
if (this.needsRebuild || j != this.prevCellX || k != this.prevCellZ || relativeCameraPos != this.prevRelativeCameraPos || cloudStatus != this.prevType) {
this.needsRebuild = false;
this.prevCellX = j;
this.prevCellZ = k;
this.prevRelativeCameraPos = relativeCameraPos;
this.prevType = cloudStatus;
MeshData meshData = this.buildMesh(Tesselator.getInstance(), j, k, cloudStatus, relativeCameraPos, renderType);
if (meshData != null) {
this.vertexBuffer.upload(meshData);
this.vertexBufferEmpty = false;
} else {
this.vertexBufferEmpty = true;
}
}
if (!this.vertexBufferEmpty) {
RenderSystem.setShaderColor(ARGB.redFloat(height), ARGB.greenFloat(height), ARGB.blueFloat(height), 1.0F);
if (cloudStatus == CloudStatus.FANCY) {
this.drawWithRenderType(RenderType.cloudsDepthOnly(), frustumMatrix, projectionMatrix, l, f, m);
}
this.drawWithRenderType(renderType, frustumMatrix, projectionMatrix, l, f, m);
VertexBuffer.unbind();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
}
}
}
private void drawWithRenderType(RenderType renderType, Matrix4f frustumMatrix, Matrix4f projectionMatrix, float x, float y, float z) {
renderType.setupRenderState();
CompiledShaderProgram compiledShaderProgram = RenderSystem.getShader();
if (compiledShaderProgram != null && compiledShaderProgram.MODEL_OFFSET != null) {
compiledShaderProgram.MODEL_OFFSET.set(-x, y, -z);
}
this.vertexBuffer.drawWithShader(frustumMatrix, projectionMatrix, compiledShaderProgram);
renderType.clearRenderState();
}
@Nullable
private MeshData buildMesh(
Tesselator tesselator, int cellX, int cellZ, CloudStatus cloudStatus, CloudRenderer.RelativeCameraPos relativeCameraPos, RenderType renderType
) {
float f = 0.8F;
int i = ARGB.colorFromFloat(0.8F, 1.0F, 1.0F, 1.0F);
int j = ARGB.colorFromFloat(0.8F, 0.9F, 0.9F, 0.9F);
int k = ARGB.colorFromFloat(0.8F, 0.7F, 0.7F, 0.7F);
int l = ARGB.colorFromFloat(0.8F, 0.8F, 0.8F, 0.8F);
BufferBuilder bufferBuilder = tesselator.begin(renderType.mode(), renderType.format());
this.buildMesh(relativeCameraPos, bufferBuilder, cellX, cellZ, k, i, j, l, cloudStatus == CloudStatus.FANCY);
return bufferBuilder.build();
}
private void buildMesh(
CloudRenderer.RelativeCameraPos relativeCameraPos,
BufferBuilder bufferBuilder,
int cellX,
int cellZ,
int bottomColor,
int topColor,
int sideColor,
int frontColor,
boolean fancyClouds
) {
if (this.texture != null) {
int i = 32;
long[] ls = this.texture.cells;
int j = this.texture.width;
int k = this.texture.height;
for (int l = -32; l <= 32; l++) {
for (int m = -32; m <= 32; m++) {
int n = Math.floorMod(cellX + m, j);
int o = Math.floorMod(cellZ + l, k);
long p = ls[n + o * j];
if (p != 0L) {
int q = getColor(p);
if (fancyClouds) {
this.buildExtrudedCell(
relativeCameraPos,
bufferBuilder,
ARGB.multiply(bottomColor, q),
ARGB.multiply(topColor, q),
ARGB.multiply(sideColor, q),
ARGB.multiply(frontColor, q),
m,
l,
p
);
} else {
this.buildFlatCell(bufferBuilder, ARGB.multiply(topColor, q), m, l);
}
}
}
}
}
}
private void buildFlatCell(BufferBuilder bufferBuilder, int color, int x, int y) {
float f = x * 12.0F;
float g = f + 12.0F;
float h = y * 12.0F;
float i = h + 12.0F;
bufferBuilder.addVertex(f, 0.0F, h).setColor(color);
bufferBuilder.addVertex(f, 0.0F, i).setColor(color);
bufferBuilder.addVertex(g, 0.0F, i).setColor(color);
bufferBuilder.addVertex(g, 0.0F, h).setColor(color);
}
private void buildExtrudedCell(
CloudRenderer.RelativeCameraPos relativeCameraPos,
BufferBuilder bufferBuilder,
int bottomColor,
int topColor,
int sideColor,
int frontColor,
int x,
int y,
long cellData
) {
float f = x * 12.0F;
float g = f + 12.0F;
float h = 0.0F;
float i = 4.0F;
float j = y * 12.0F;
float k = j + 12.0F;
if (relativeCameraPos != CloudRenderer.RelativeCameraPos.BELOW_CLOUDS) {
bufferBuilder.addVertex(f, 4.0F, j).setColor(topColor);
bufferBuilder.addVertex(f, 4.0F, k).setColor(topColor);
bufferBuilder.addVertex(g, 4.0F, k).setColor(topColor);
bufferBuilder.addVertex(g, 4.0F, j).setColor(topColor);
}
if (relativeCameraPos != CloudRenderer.RelativeCameraPos.ABOVE_CLOUDS) {
bufferBuilder.addVertex(g, 0.0F, j).setColor(bottomColor);
bufferBuilder.addVertex(g, 0.0F, k).setColor(bottomColor);
bufferBuilder.addVertex(f, 0.0F, k).setColor(bottomColor);
bufferBuilder.addVertex(f, 0.0F, j).setColor(bottomColor);
}
if (isNorthEmpty(cellData) && y > 0) {
bufferBuilder.addVertex(f, 0.0F, j).setColor(frontColor);
bufferBuilder.addVertex(f, 4.0F, j).setColor(frontColor);
bufferBuilder.addVertex(g, 4.0F, j).setColor(frontColor);
bufferBuilder.addVertex(g, 0.0F, j).setColor(frontColor);
}
if (isSouthEmpty(cellData) && y < 0) {
bufferBuilder.addVertex(g, 0.0F, k).setColor(frontColor);
bufferBuilder.addVertex(g, 4.0F, k).setColor(frontColor);
bufferBuilder.addVertex(f, 4.0F, k).setColor(frontColor);
bufferBuilder.addVertex(f, 0.0F, k).setColor(frontColor);
}
if (isWestEmpty(cellData) && x > 0) {
bufferBuilder.addVertex(f, 0.0F, k).setColor(sideColor);
bufferBuilder.addVertex(f, 4.0F, k).setColor(sideColor);
bufferBuilder.addVertex(f, 4.0F, j).setColor(sideColor);
bufferBuilder.addVertex(f, 0.0F, j).setColor(sideColor);
}
if (isEastEmpty(cellData) && x < 0) {
bufferBuilder.addVertex(g, 0.0F, j).setColor(sideColor);
bufferBuilder.addVertex(g, 4.0F, j).setColor(sideColor);
bufferBuilder.addVertex(g, 4.0F, k).setColor(sideColor);
bufferBuilder.addVertex(g, 0.0F, k).setColor(sideColor);
}
boolean bl = Math.abs(x) <= 1 && Math.abs(y) <= 1;
if (bl) {
bufferBuilder.addVertex(g, 4.0F, j).setColor(topColor);
bufferBuilder.addVertex(g, 4.0F, k).setColor(topColor);
bufferBuilder.addVertex(f, 4.0F, k).setColor(topColor);
bufferBuilder.addVertex(f, 4.0F, j).setColor(topColor);
bufferBuilder.addVertex(f, 0.0F, j).setColor(bottomColor);
bufferBuilder.addVertex(f, 0.0F, k).setColor(bottomColor);
bufferBuilder.addVertex(g, 0.0F, k).setColor(bottomColor);
bufferBuilder.addVertex(g, 0.0F, j).setColor(bottomColor);
bufferBuilder.addVertex(g, 0.0F, j).setColor(frontColor);
bufferBuilder.addVertex(g, 4.0F, j).setColor(frontColor);
bufferBuilder.addVertex(f, 4.0F, j).setColor(frontColor);
bufferBuilder.addVertex(f, 0.0F, j).setColor(frontColor);
bufferBuilder.addVertex(f, 0.0F, k).setColor(frontColor);
bufferBuilder.addVertex(f, 4.0F, k).setColor(frontColor);
bufferBuilder.addVertex(g, 4.0F, k).setColor(frontColor);
bufferBuilder.addVertex(g, 0.0F, k).setColor(frontColor);
bufferBuilder.addVertex(f, 0.0F, j).setColor(sideColor);
bufferBuilder.addVertex(f, 4.0F, j).setColor(sideColor);
bufferBuilder.addVertex(f, 4.0F, k).setColor(sideColor);
bufferBuilder.addVertex(f, 0.0F, k).setColor(sideColor);
bufferBuilder.addVertex(g, 0.0F, k).setColor(sideColor);
bufferBuilder.addVertex(g, 4.0F, k).setColor(sideColor);
bufferBuilder.addVertex(g, 4.0F, j).setColor(sideColor);
bufferBuilder.addVertex(g, 0.0F, j).setColor(sideColor);
}
}
public void markForRebuild() {
this.needsRebuild = true;
}
public void close() {
this.vertexBuffer.close();
}
@Environment(EnvType.CLIENT)
static enum RelativeCameraPos {
ABOVE_CLOUDS,
INSIDE_CLOUDS,
BELOW_CLOUDS;
}
@Environment(EnvType.CLIENT)
public record TextureData(long[] cells, int width, int height) {
}
}