205 lines
12 KiB
Java
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) {
|
|
}
|
|
}
|
|
|