/* * 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 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) { } }