2025-11-24 22:52:51 +03:00

205 lines
12 KiB
Java

/*
* Decompiled with CFR 0.152.
*/
package net.minecraft.client.renderer;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.List;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.rendertype.RenderType;
import net.minecraft.client.renderer.rendertype.RenderTypes;
import net.minecraft.client.renderer.state.WeatherRenderState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.SectionPos;
import net.minecraft.core.Vec3i;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.resources.Identifier;
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;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
public class WeatherEffectRenderer {
private static final float RAIN_PARTICLES_PER_BLOCK = 0.225f;
private static final int RAIN_RADIUS = 10;
private static final Identifier RAIN_LOCATION = Identifier.withDefaultNamespace("textures/environment/rain.png");
private static final Identifier SNOW_LOCATION = Identifier.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 z = 0; z < 32; ++z) {
for (int x = 0; x < 32; ++x) {
float deltaX = x - 16;
float deltaZ = z - 16;
float distance = Mth.length(deltaX, deltaZ);
this.columnSizeX[z * 32 + x] = -deltaZ / distance;
this.columnSizeZ[z * 32 + x] = deltaX / distance;
}
}
}
public void extractRenderState(Level level, int ticks, float partialTicks, Vec3 cameraPos, WeatherRenderState renderState) {
renderState.intensity = level.getRainLevel(partialTicks);
if (renderState.intensity <= 0.0f) {
return;
}
renderState.radius = Minecraft.getInstance().options.weatherRadius().get();
int cameraBlockX = Mth.floor(cameraPos.x);
int cameraBlockY = Mth.floor(cameraPos.y);
int cameraBlockZ = Mth.floor(cameraPos.z);
BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
RandomSource random = RandomSource.create();
for (int z = cameraBlockZ - renderState.radius; z <= cameraBlockZ + renderState.radius; ++z) {
for (int x = cameraBlockX - renderState.radius; x <= cameraBlockX + renderState.radius; ++x) {
Biome.Precipitation precipitation;
int terrainHeight = level.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z);
int y0 = Math.max(cameraBlockY - renderState.radius, terrainHeight);
int y1 = Math.max(cameraBlockY + renderState.radius, terrainHeight);
if (y1 - y0 == 0 || (precipitation = this.getPrecipitationAt(level, mutablePos.set(x, cameraBlockY, z))) == Biome.Precipitation.NONE) continue;
int seed = x * x * 3121 + x * 45238971 ^ z * z * 418711 + z * 13761;
random.setSeed(seed);
int lightSampleY = Math.max(cameraBlockY, terrainHeight);
int lightCoords = LevelRenderer.getLightColor(level, mutablePos.set(x, lightSampleY, z));
if (precipitation == Biome.Precipitation.RAIN) {
renderState.rainColumns.add(this.createRainColumnInstance(random, ticks, x, y0, y1, z, lightCoords, partialTicks));
continue;
}
if (precipitation != Biome.Precipitation.SNOW) continue;
renderState.snowColumns.add(this.createSnowColumnInstance(random, ticks, x, y0, y1, z, lightCoords, partialTicks));
}
}
}
public void render(MultiBufferSource bufferSource, Vec3 cameraPos, WeatherRenderState renderState) {
RenderType renderType;
if (!renderState.rainColumns.isEmpty()) {
renderType = RenderTypes.weather(RAIN_LOCATION, Minecraft.useShaderTransparency());
this.renderInstances(bufferSource.getBuffer(renderType), renderState.rainColumns, cameraPos, 1.0f, renderState.radius, renderState.intensity);
}
if (!renderState.snowColumns.isEmpty()) {
renderType = RenderTypes.weather(SNOW_LOCATION, Minecraft.useShaderTransparency());
this.renderInstances(bufferSource.getBuffer(renderType), renderState.snowColumns, cameraPos, 0.8f, renderState.radius, renderState.intensity);
}
}
private ColumnInstance createRainColumnInstance(RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTicks) {
int wrappedTicks = ticks & 0x1FFFF;
int tickOffset = x * x * 3121 + x * 45238971 + z * z * 418711 + z * 13761 & 0xFF;
float blockPosRainSpeed = 3.0f + random.nextFloat();
float textureOffset = -((float)(wrappedTicks + tickOffset) + partialTicks) / 32.0f * blockPosRainSpeed;
float wrappedTextureOffset = textureOffset % 32.0f;
return new ColumnInstance(x, z, bottomY, topY, 0.0f, wrappedTextureOffset, lightCoords);
}
private ColumnInstance createSnowColumnInstance(RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTicks) {
float time = (float)ticks + partialTicks;
float u = (float)(random.nextDouble() + (double)(time * 0.01f * (float)random.nextGaussian()));
float v = (float)(random.nextDouble() + (double)(time * (float)random.nextGaussian() * 0.001f));
float vOffset = -((float)(ticks & 0x1FF) + partialTicks) / 512.0f;
int brightenedLightCoords = LightTexture.pack((LightTexture.block(lightCoords) * 3 + 15) / 4, (LightTexture.sky(lightCoords) * 3 + 15) / 4);
return new ColumnInstance(x, z, bottomY, topY, u, vOffset + v, brightenedLightCoords);
}
private void renderInstances(VertexConsumer builder, List<ColumnInstance> columns, Vec3 cameraPos, float maxAlpha, int radius, float intensity) {
float radiusSq = radius * radius;
for (ColumnInstance column : columns) {
float relativeX = (float)((double)column.x + 0.5 - cameraPos.x);
float relativeZ = (float)((double)column.z + 0.5 - cameraPos.z);
float distanceSq = (float)Mth.lengthSquared(relativeX, relativeZ);
float alpha = Mth.lerp(Math.min(distanceSq / radiusSq, 1.0f), maxAlpha, 0.5f) * intensity;
int color = ARGB.white(alpha);
int index = (column.z - Mth.floor(cameraPos.z) + 16) * 32 + column.x - Mth.floor(cameraPos.x) + 16;
float halfSizeX = this.columnSizeX[index] / 2.0f;
float halfSizeZ = this.columnSizeZ[index] / 2.0f;
float x0 = relativeX - halfSizeX;
float x1 = relativeX + halfSizeX;
float y1 = (float)((double)column.topY - cameraPos.y);
float y0 = (float)((double)column.bottomY - cameraPos.y);
float z0 = relativeZ - halfSizeZ;
float z1 = relativeZ + halfSizeZ;
float u0 = column.uOffset + 0.0f;
float u1 = column.uOffset + 1.0f;
float v0 = (float)column.bottomY * 0.25f + column.vOffset;
float v1 = (float)column.topY * 0.25f + column.vOffset;
builder.addVertex(x0, y1, z0).setUv(u0, v0).setColor(color).setLight(column.lightCoords);
builder.addVertex(x1, y1, z1).setUv(u1, v0).setColor(color).setLight(column.lightCoords);
builder.addVertex(x1, y0, z1).setUv(u1, v1).setColor(color).setLight(column.lightCoords);
builder.addVertex(x0, y0, z0).setUv(u0, v1).setColor(color).setLight(column.lightCoords);
}
}
public void tickRainParticles(ClientLevel level, Camera camera, int ticks, ParticleStatus particleStatus, int weatherRadius) {
float rainLevel = level.getRainLevel(1.0f);
if (rainLevel <= 0.0f) {
return;
}
RandomSource random = RandomSource.create((long)ticks * 312987231L);
BlockPos cameraPosition = BlockPos.containing(camera.position());
Vec3i rainParticlePosition = null;
int weatherDiameter = 2 * weatherRadius + 1;
int weatherArea = weatherDiameter * weatherDiameter;
int rainParticles = (int)(0.225f * (float)weatherArea * rainLevel * rainLevel) / (particleStatus == ParticleStatus.DECREASED ? 2 : 1);
for (int ii = 0; ii < rainParticles; ++ii) {
int z;
int x = random.nextInt(weatherDiameter) - weatherRadius;
BlockPos heightmapPosition = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, cameraPosition.offset(x, 0, z = random.nextInt(weatherDiameter) - weatherRadius));
if (heightmapPosition.getY() <= level.getMinY() || heightmapPosition.getY() > cameraPosition.getY() + 10 || heightmapPosition.getY() < cameraPosition.getY() - 10 || this.getPrecipitationAt(level, heightmapPosition) != Biome.Precipitation.RAIN) continue;
rainParticlePosition = heightmapPosition.below();
if (particleStatus == ParticleStatus.MINIMAL) break;
double blockX = random.nextDouble();
double blockZ = random.nextDouble();
BlockState block = level.getBlockState((BlockPos)rainParticlePosition);
FluidState fluid = level.getFluidState((BlockPos)rainParticlePosition);
VoxelShape blockShape = block.getCollisionShape(level, (BlockPos)rainParticlePosition);
double blockTop = blockShape.max(Direction.Axis.Y, blockX, blockZ);
double fluidTop = fluid.getHeight(level, (BlockPos)rainParticlePosition);
double particleY = Math.max(blockTop, fluidTop);
SimpleParticleType particleType = fluid.is(FluidTags.LAVA) || block.is(Blocks.MAGMA_BLOCK) || CampfireBlock.isLitCampfire(block) ? ParticleTypes.SMOKE : ParticleTypes.RAIN;
level.addParticle(particleType, (double)rainParticlePosition.getX() + blockX, (double)rainParticlePosition.getY() + particleY, (double)rainParticlePosition.getZ() + blockZ, 0.0, 0.0, 0.0);
}
if (rainParticlePosition != null && random.nextInt(3) < this.rainSoundTime++) {
this.rainSoundTime = 0;
if (rainParticlePosition.getY() > cameraPosition.getY() + 1 && level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, cameraPosition).getY() > Mth.floor(cameraPosition.getY())) {
level.playLocalSound((BlockPos)rainParticlePosition, SoundEvents.WEATHER_RAIN_ABOVE, SoundSource.WEATHER, 0.1f, 0.5f, false);
} else {
level.playLocalSound((BlockPos)rainParticlePosition, 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;
}
Biome biome = level.getBiome(pos).value();
return biome.getPrecipitationAt(pos, level.getSeaLevel());
}
public record ColumnInstance(int x, int z, int bottomY, int topY, float uOffset, float vOffset, int lightCoords) {
}
}