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

238 lines
10 KiB
Java

package net.minecraft.client.renderer;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.ArrayList;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.CampfireBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
@Environment(EnvType.CLIENT)
public class WeatherEffectRenderer {
private static final int RAIN_RADIUS = 10;
private static final int RAIN_DIAMETER = 21;
private static final ResourceLocation RAIN_LOCATION = ResourceLocation.withDefaultNamespace("textures/environment/rain.png");
private static final ResourceLocation SNOW_LOCATION = ResourceLocation.withDefaultNamespace("textures/environment/snow.png");
private static final int RAIN_TABLE_SIZE = 32;
private static final int HALF_RAIN_TABLE_SIZE = 16;
private int rainSoundTime;
private final float[] columnSizeX = new float[1024];
private final float[] columnSizeZ = new float[1024];
public WeatherEffectRenderer() {
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 32; j++) {
float f = j - 16;
float g = i - 16;
float h = Mth.length(f, g);
this.columnSizeX[i * 32 + j] = -g / h;
this.columnSizeZ[i * 32 + j] = f / h;
}
}
}
public void render(Level level, MultiBufferSource bufferSource, int ticks, float partialTick, Vec3 cameraPosition) {
float f = level.getRainLevel(partialTick);
if (!(f <= 0.0F)) {
int i = Minecraft.useFancyGraphics() ? 10 : 5;
List<WeatherEffectRenderer.ColumnInstance> list = new ArrayList();
List<WeatherEffectRenderer.ColumnInstance> list2 = new ArrayList();
this.collectColumnInstances(level, ticks, partialTick, cameraPosition, i, list, list2);
if (!list.isEmpty() || !list2.isEmpty()) {
this.render(bufferSource, cameraPosition, i, f, list, list2);
}
}
}
private void collectColumnInstances(
Level level,
int ticks,
float partialTick,
Vec3 cameraPosition,
int radius,
List<WeatherEffectRenderer.ColumnInstance> rainColumnInstances,
List<WeatherEffectRenderer.ColumnInstance> snowColumnInstances
) {
int i = Mth.floor(cameraPosition.x);
int j = Mth.floor(cameraPosition.y);
int k = Mth.floor(cameraPosition.z);
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
RandomSource randomSource = RandomSource.create();
for (int l = k - radius; l <= k + radius; l++) {
for (int m = i - radius; m <= i + radius; m++) {
int n = level.getHeight(Types.MOTION_BLOCKING, m, l);
int o = Math.max(j - radius, n);
int p = Math.max(j + radius, n);
if (p - o != 0) {
Biome.Precipitation precipitation = this.getPrecipitationAt(level, mutableBlockPos.set(m, j, l));
if (precipitation != Biome.Precipitation.NONE) {
int q = m * m * 3121 + m * 45238971 ^ l * l * 418711 + l * 13761;
randomSource.setSeed(q);
int r = Math.max(j, n);
int s = LevelRenderer.getLightColor(level, mutableBlockPos.set(m, r, l));
if (precipitation == Biome.Precipitation.RAIN) {
rainColumnInstances.add(this.createRainColumnInstance(randomSource, ticks, m, o, p, l, s, partialTick));
} else if (precipitation == Biome.Precipitation.SNOW) {
snowColumnInstances.add(this.createSnowColumnInstance(randomSource, ticks, m, o, p, l, s, partialTick));
}
}
}
}
}
}
private void render(
MultiBufferSource bufferSource,
Vec3 cameraPosition,
int radius,
float rainLevel,
List<WeatherEffectRenderer.ColumnInstance> rainColumnInstances,
List<WeatherEffectRenderer.ColumnInstance> snowColumnInstances
) {
if (!rainColumnInstances.isEmpty()) {
RenderType renderType = RenderType.weather(RAIN_LOCATION, Minecraft.useShaderTransparency());
this.renderInstances(bufferSource.getBuffer(renderType), rainColumnInstances, cameraPosition, 1.0F, radius, rainLevel);
}
if (!snowColumnInstances.isEmpty()) {
RenderType renderType = RenderType.weather(SNOW_LOCATION, Minecraft.useShaderTransparency());
this.renderInstances(bufferSource.getBuffer(renderType), snowColumnInstances, cameraPosition, 0.8F, radius, rainLevel);
}
}
private WeatherEffectRenderer.ColumnInstance createRainColumnInstance(
RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTick
) {
int i = ticks & 131071;
int j = x * x * 3121 + x * 45238971 + z * z * 418711 + z * 13761 & 0xFF;
float f = 3.0F + random.nextFloat();
float g = -(i + j + partialTick) / 32.0F * f;
float h = g % 32.0F;
return new WeatherEffectRenderer.ColumnInstance(x, z, bottomY, topY, 0.0F, h, lightCoords);
}
private WeatherEffectRenderer.ColumnInstance createSnowColumnInstance(
RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTick
) {
float f = ticks + partialTick;
float g = (float)(random.nextDouble() + f * 0.01F * (float)random.nextGaussian());
float h = (float)(random.nextDouble() + f * (float)random.nextGaussian() * 0.001F);
float i = -((ticks & 511) + partialTick) / 512.0F;
int j = LightTexture.pack((LightTexture.block(lightCoords) * 3 + 15) / 4, (LightTexture.sky(lightCoords) * 3 + 15) / 4);
return new WeatherEffectRenderer.ColumnInstance(x, z, bottomY, topY, g, i + h, j);
}
private void renderInstances(
VertexConsumer buffer, List<WeatherEffectRenderer.ColumnInstance> columnInstances, Vec3 cameraPosition, float amount, int radius, float rainLevel
) {
for (WeatherEffectRenderer.ColumnInstance columnInstance : columnInstances) {
float f = (float)(columnInstance.x + 0.5 - cameraPosition.x);
float g = (float)(columnInstance.z + 0.5 - cameraPosition.z);
float h = (float)Mth.lengthSquared(f, g);
float i = Mth.lerp(h / (radius * radius), amount, 0.5F) * rainLevel;
int j = ARGB.white(i);
int k = (columnInstance.z - Mth.floor(cameraPosition.z) + 16) * 32 + columnInstance.x - Mth.floor(cameraPosition.x) + 16;
float l = this.columnSizeX[k] / 2.0F;
float m = this.columnSizeZ[k] / 2.0F;
float n = f - l;
float o = f + l;
float p = (float)(columnInstance.topY - cameraPosition.y);
float q = (float)(columnInstance.bottomY - cameraPosition.y);
float r = g - m;
float s = g + m;
float t = columnInstance.uOffset + 0.0F;
float u = columnInstance.uOffset + 1.0F;
float v = columnInstance.bottomY * 0.25F + columnInstance.vOffset;
float w = columnInstance.topY * 0.25F + columnInstance.vOffset;
buffer.addVertex(n, p, r).setUv(t, v).setColor(j).setLight(columnInstance.lightCoords);
buffer.addVertex(o, p, s).setUv(u, v).setColor(j).setLight(columnInstance.lightCoords);
buffer.addVertex(o, q, s).setUv(u, w).setColor(j).setLight(columnInstance.lightCoords);
buffer.addVertex(n, q, r).setUv(t, w).setColor(j).setLight(columnInstance.lightCoords);
}
}
public void tickRainParticles(ClientLevel level, Camera camera, int ticks, ParticleStatus particleStatus) {
float f = level.getRainLevel(1.0F) / (Minecraft.useFancyGraphics() ? 1.0F : 2.0F);
if (!(f <= 0.0F)) {
RandomSource randomSource = RandomSource.create(ticks * 312987231L);
BlockPos blockPos = BlockPos.containing(camera.getPosition());
BlockPos blockPos2 = null;
int i = (int)(100.0F * f * f) / (particleStatus == ParticleStatus.DECREASED ? 2 : 1);
for (int j = 0; j < i; j++) {
int k = randomSource.nextInt(21) - 10;
int l = randomSource.nextInt(21) - 10;
BlockPos blockPos3 = level.getHeightmapPos(Types.MOTION_BLOCKING, blockPos.offset(k, 0, l));
if (blockPos3.getY() > level.getMinY()
&& blockPos3.getY() <= blockPos.getY() + 10
&& blockPos3.getY() >= blockPos.getY() - 10
&& this.getPrecipitationAt(level, blockPos3) == Biome.Precipitation.RAIN) {
blockPos2 = blockPos3.below();
if (particleStatus == ParticleStatus.MINIMAL) {
break;
}
double d = randomSource.nextDouble();
double e = randomSource.nextDouble();
BlockState blockState = level.getBlockState(blockPos2);
FluidState fluidState = level.getFluidState(blockPos2);
VoxelShape voxelShape = blockState.getCollisionShape(level, blockPos2);
double g = voxelShape.max(Axis.Y, d, e);
double h = fluidState.getHeight(level, blockPos2);
double m = Math.max(g, h);
ParticleOptions particleOptions = !fluidState.is(FluidTags.LAVA) && !blockState.is(Blocks.MAGMA_BLOCK) && !CampfireBlock.isLitCampfire(blockState)
? ParticleTypes.RAIN
: ParticleTypes.SMOKE;
level.addParticle(particleOptions, blockPos2.getX() + d, blockPos2.getY() + m, blockPos2.getZ() + e, 0.0, 0.0, 0.0);
}
}
if (blockPos2 != null && randomSource.nextInt(3) < this.rainSoundTime++) {
this.rainSoundTime = 0;
if (blockPos2.getY() > blockPos.getY() + 1 && level.getHeightmapPos(Types.MOTION_BLOCKING, blockPos).getY() > Mth.floor((float)blockPos.getY())) {
level.playLocalSound(blockPos2, SoundEvents.WEATHER_RAIN_ABOVE, SoundSource.WEATHER, 0.1F, 0.5F, false);
} else {
level.playLocalSound(blockPos2, SoundEvents.WEATHER_RAIN, SoundSource.WEATHER, 0.2F, 1.0F, false);
}
}
}
}
private Biome.Precipitation getPrecipitationAt(Level level, BlockPos pos) {
if (!level.getChunkSource().hasChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()))) {
return Biome.Precipitation.NONE;
} else {
Biome biome = level.getBiome(pos).value();
return biome.getPrecipitationAt(pos, level.getSeaLevel());
}
}
@Environment(EnvType.CLIENT)
record ColumnInstance(int x, int z, int bottomY, int topY, float uOffset, float vOffset, int lightCoords) {
}
}