WeatherEffectRenderer.java

net.minecraft.client.renderer.WeatherEffectRenderer

信息

  • 全限定名:net.minecraft.client.renderer.WeatherEffectRenderer
  • 类型:public class
  • 包:net.minecraft.client.renderer
  • 源码路径:src/main/java/net/minecraft/client/renderer/WeatherEffectRenderer.java
  • 起始行号:L57
  • 职责:

    TODO

字段/常量

  • RAIN_PARTICLES_PER_BLOCK

    • 类型: float
    • 修饰符: private static final
    • 源码定位: L58
    • 说明:

      TODO

  • RAIN_RADIUS

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L59
    • 说明:

      TODO

  • RAIN_LOCATION

    • 类型: Identifier
    • 修饰符: private static final
    • 源码定位: L60
    • 说明:

      TODO

  • SNOW_LOCATION

    • 类型: Identifier
    • 修饰符: private static final
    • 源码定位: L61
    • 说明:

      TODO

  • RAIN_TABLE_SIZE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L62
    • 说明:

      TODO

  • HALF_RAIN_TABLE_SIZE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L63
    • 说明:

      TODO

  • INDICES_PER_COLUMN

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L64
    • 说明:

      TODO

  • rainSoundTime

    • 类型: int
    • 修饰符: private
    • 源码定位: L65
    • 说明:

      TODO

  • columnSizeX

    • 类型: float[]
    • 修饰符: private final
    • 源码定位: L66
    • 说明:

      TODO

  • columnSizeZ

    • 类型: float[]
    • 修饰符: private final
    • 源码定位: L67
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.renderer.WeatherEffectRenderer.ColumnInstance
    • 类型: record
    • 修饰符: public
    • 源码定位: L290
    • 说明:

      TODO

构造器

public WeatherEffectRenderer() @ L69

  • 构造器名:WeatherEffectRenderer
  • 源码定位:L69
  • 修饰符:public

参数:

说明:

TODO

方法

下面的方法块按源码顺序生成。

public void extractRenderState(Level level, int ticks, float partialTicks, Vec3 cameraPos, WeatherRenderState renderState) @ L81

  • 方法名:extractRenderState
  • 源码定位:L81
  • 返回类型:void
  • 修饰符:public

参数:

  • level: Level
  • ticks: int
  • partialTicks: float
  • cameraPos: Vec3
  • renderState: WeatherRenderState

说明:

TODO

private void renderWeather(RenderPass renderPass, AbstractTexture texture, int startColumn, int columnCount) @ L115

  • 方法名:renderWeather
  • 源码定位:L115
  • 返回类型:void
  • 修饰符:private

参数:

  • renderPass: RenderPass
  • texture: AbstractTexture
  • startColumn: int
  • columnCount: int

说明:

TODO

public void render(Vec3 cameraPos, WeatherRenderState renderState) @ L120

  • 方法名:render
  • 源码定位:L120
  • 返回类型:void
  • 修饰符:public

参数:

  • cameraPos: Vec3
  • renderState: WeatherRenderState

说明:

TODO

private WeatherEffectRenderer.ColumnInstance createRainColumnInstance(RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTicks) @ L167

  • 方法名:createRainColumnInstance
  • 源码定位:L167
  • 返回类型:WeatherEffectRenderer.ColumnInstance
  • 修饰符:private

参数:

  • random: RandomSource
  • ticks: int
  • x: int
  • bottomY: int
  • topY: int
  • z: int
  • lightCoords: int
  • partialTicks: float

说明:

TODO

private WeatherEffectRenderer.ColumnInstance createSnowColumnInstance(RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTicks) @ L178

  • 方法名:createSnowColumnInstance
  • 源码定位:L178
  • 返回类型:WeatherEffectRenderer.ColumnInstance
  • 修饰符:private

参数:

  • random: RandomSource
  • ticks: int
  • x: int
  • bottomY: int
  • topY: int
  • z: int
  • lightCoords: int
  • partialTicks: float

说明:

TODO

private void renderInstances(VertexConsumer builder, List<WeatherEffectRenderer.ColumnInstance> columns, Vec3 cameraPos, float maxAlpha, int radius, float intensity) @ L189

  • 方法名:renderInstances
  • 源码定位:L189
  • 返回类型:void
  • 修饰符:private

参数:

  • builder: VertexConsumer
  • columns: List<WeatherEffectRenderer.ColumnInstance>
  • cameraPos: Vec3
  • maxAlpha: float
  • radius: int
  • intensity: float

说明:

TODO

public void tickRainParticles(ClientLevel level, Camera camera, int ticks, ParticleStatus particleStatus, int weatherRadius) @ L222

  • 方法名:tickRainParticles
  • 源码定位:L222
  • 返回类型:void
  • 修饰符:public

参数:

  • level: ClientLevel
  • camera: Camera
  • ticks: int
  • particleStatus: ParticleStatus
  • weatherRadius: int

说明:

TODO

private Biome.Precipitation getPrecipitationAt(Level level, BlockPos pos) @ L280

  • 方法名:getPrecipitationAt
  • 源码定位:L280
  • 返回类型:Biome.Precipitation
  • 修饰符:private

参数:

  • level: Level
  • pos: BlockPos

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
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 static final int INDICES_PER_COLUMN = 6;
    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)) {
            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.createThreadLocalInstance();
 
            for (int z = cameraBlockZ - renderState.radius; z <= cameraBlockZ + renderState.radius; z++) {
                for (int x = cameraBlockX - renderState.radius; x <= cameraBlockX + renderState.radius; x++) {
                    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) {
                        Biome.Precipitation precipitation = this.getPrecipitationAt(level, mutablePos.set(x, cameraBlockY, z));
                        if (precipitation != Biome.Precipitation.NONE) {
                            int seed = x * x * 3121 + x * 45238971 ^ z * z * 418711 + z * 13761;
                            random.setSeed(seed);
                            int lightSampleY = Math.max(cameraBlockY, terrainHeight);
                            int lightCoords = LevelRenderer.getLightCoords(level, mutablePos.set(x, lightSampleY, z));
                            if (precipitation == Biome.Precipitation.RAIN) {
                                renderState.rainColumns.add(this.createRainColumnInstance(random, ticks, x, y0, y1, z, lightCoords, partialTicks));
                            } else if (precipitation == Biome.Precipitation.SNOW) {
                                renderState.snowColumns.add(this.createSnowColumnInstance(random, ticks, x, y0, y1, z, lightCoords, partialTicks));
                            }
                        }
                    }
                }
            }
        }
    }
 
    private void renderWeather(RenderPass renderPass, AbstractTexture texture, int startColumn, int columnCount) {
        renderPass.bindTexture("Sampler0", texture.getTextureView(), texture.getSampler());
        renderPass.drawIndexed(0, startColumn * 6, columnCount * 6, 1);
    }
 
    public void render(Vec3 cameraPos, WeatherRenderState renderState) {
        int columnCount = renderState.rainColumns.size() + renderState.snowColumns.size();
        if (columnCount != 0) {
            TextureManager textureManager = Minecraft.getInstance().getTextureManager();
            AbstractTexture rainTexture = textureManager.getTexture(RAIN_LOCATION);
            AbstractTexture snowTexture = textureManager.getTexture(SNOW_LOCATION);
            RenderTarget weatherRenderTarget = OutputTarget.WEATHER_TARGET.getRenderTarget();
            GpuTextureView colorTexture = weatherRenderTarget.getColorTextureView();
            GpuTextureView depthTexture = weatherRenderTarget.getDepthTextureView();
            RenderPipeline renderPipeline = Minecraft.useShaderTransparency() ? RenderPipelines.WEATHER_DEPTH_WRITE : RenderPipelines.WEATHER_NO_DEPTH_WRITE;
 
            GpuBuffer vertexBuffer;
            GpuBuffer indexBuffer;
            VertexFormat.IndexType indexType;
            try (ByteBufferBuilder builder = ByteBufferBuilder.exactlySized(columnCount * DefaultVertexFormat.PARTICLE.getVertexSize() * 4)) {
                BufferBuilder bufferBuilder = new BufferBuilder(builder, VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE);
                this.renderInstances(bufferBuilder, renderState.rainColumns, cameraPos, 1.0F, renderState.radius, renderState.intensity);
                this.renderInstances(bufferBuilder, renderState.snowColumns, cameraPos, 0.8F, renderState.radius, renderState.intensity);
 
                try (MeshData mesh = bufferBuilder.buildOrThrow()) {
                    vertexBuffer = renderPipeline.getVertexFormat().uploadImmediateVertexBuffer(mesh.vertexBuffer());
                    RenderSystem.AutoStorageIndexBuffer autoIndices = RenderSystem.getSequentialBuffer(mesh.drawState().mode());
                    indexBuffer = autoIndices.getBuffer(mesh.drawState().indexCount());
                    indexType = autoIndices.type();
                }
            }
 
            GpuBufferSlice dynamicTransforms = RenderSystem.getDynamicUniforms()
                .writeTransform(RenderSystem.getModelViewMatrix(), new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f());
 
            try (RenderPass renderPass = RenderSystem.getDevice()
                    .createCommandEncoder()
                    .createRenderPass(() -> "Weather Effect", colorTexture, OptionalInt.empty(), depthTexture, OptionalDouble.empty())) {
                renderPass.setPipeline(renderPipeline);
                RenderSystem.bindDefaultUniforms(renderPass);
                renderPass.setUniform("DynamicTransforms", dynamicTransforms);
                renderPass.bindTexture(
                    "Sampler2", Minecraft.getInstance().gameRenderer.lightmap(), RenderSystem.getSamplerCache().getClampToEdge(FilterMode.LINEAR)
                );
                renderPass.setIndexBuffer(indexBuffer, indexType);
                renderPass.setVertexBuffer(0, vertexBuffer);
                this.renderWeather(renderPass, rainTexture, 0, renderState.rainColumns.size());
                this.renderWeather(renderPass, snowTexture, renderState.rainColumns.size(), renderState.snowColumns.size());
            }
        }
    }
 
    private WeatherEffectRenderer.ColumnInstance createRainColumnInstance(
        RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTicks
    ) {
        int wrappedTicks = ticks & 131071;
        int tickOffset = x * x * 3121 + x * 45238971 + z * z * 418711 + z * 13761 & 0xFF;
        float blockPosRainSpeed = 3.0F + random.nextFloat();
        float textureOffset = -(wrappedTicks + tickOffset + partialTicks) / 32.0F * blockPosRainSpeed;
        float wrappedTextureOffset = textureOffset % 32.0F;
        return new WeatherEffectRenderer.ColumnInstance(x, z, bottomY, topY, 0.0F, wrappedTextureOffset, lightCoords);
    }
 
    private WeatherEffectRenderer.ColumnInstance createSnowColumnInstance(
        RandomSource random, int ticks, int x, int bottomY, int topY, int z, int lightCoords, float partialTicks
    ) {
        float time = ticks + partialTicks;
        float u = (float)(random.nextDouble() + time * 0.01F * (float)random.nextGaussian());
        float v = (float)(random.nextDouble() + time * (float)random.nextGaussian() * 0.001F);
        float vOffset = -((ticks & 511) + partialTicks) / 512.0F;
        int brightenedLightCoords = LightCoordsUtil.pack((LightCoordsUtil.block(lightCoords) * 3 + 15) / 4, (LightCoordsUtil.sky(lightCoords) * 3 + 15) / 4);
        return new WeatherEffectRenderer.ColumnInstance(x, z, bottomY, topY, u, vOffset + v, brightenedLightCoords);
    }
 
    private void renderInstances(
        VertexConsumer builder, List<WeatherEffectRenderer.ColumnInstance> columns, Vec3 cameraPos, float maxAlpha, int radius, float intensity
    ) {
        if (!columns.isEmpty()) {
            float radiusSq = radius * radius;
 
            for (WeatherEffectRenderer.ColumnInstance column : columns) {
                float relativeX = (float)(column.x + 0.5 - cameraPos.x);
                float relativeZ = (float)(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)(column.topY - cameraPos.y);
                float y0 = (float)(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 = column.bottomY * 0.25F + column.vOffset;
                float v1 = 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)) {
            RandomSource random = RandomSource.createThreadLocalInstance(ticks * 312987231L);
            BlockPos cameraPosition = BlockPos.containing(camera.position());
            BlockPos rainParticlePosition = null;
            int weatherDiameter = 2 * weatherRadius + 1;
            int weatherArea = weatherDiameter * weatherDiameter;
            int rainParticles = (int)(0.225F * weatherArea * rainLevel * rainLevel) / (particleStatus == ParticleStatus.DECREASED ? 2 : 1);
 
            for (int ii = 0; ii < rainParticles; ii++) {
                int x = random.nextInt(weatherDiameter) - weatherRadius;
                int z = random.nextInt(weatherDiameter) - weatherRadius;
                BlockPos heightmapPosition = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, cameraPosition.offset(x, 0, z));
                if (heightmapPosition.getY() > level.getMinY()
                    && heightmapPosition.getY() <= cameraPosition.getY() + 10
                    && heightmapPosition.getY() >= cameraPosition.getY() - 10
                    && this.getPrecipitationAt(level, heightmapPosition) == Biome.Precipitation.RAIN) {
                    rainParticlePosition = heightmapPosition.below();
                    if (particleStatus == ParticleStatus.MINIMAL) {
                        break;
                    }
 
                    double blockX = random.nextDouble();
                    double blockZ = random.nextDouble();
                    BlockState block = level.getBlockState(rainParticlePosition);
                    FluidState fluid = level.getFluidState(rainParticlePosition);
                    VoxelShape blockShape = block.getCollisionShape(level, rainParticlePosition);
                    double blockTop = blockShape.max(Direction.Axis.Y, blockX, blockZ);
                    double fluidTop = fluid.getHeight(level, rainParticlePosition);
                    double particleY = Math.max(blockTop, fluidTop);
                    ParticleOptions particleType = !fluid.is(FluidTags.LAVA) && !block.is(Blocks.MAGMA_BLOCK) && !CampfireBlock.isLitCampfire(block)
                        ? ParticleTypes.RAIN
                        : ParticleTypes.SMOKE;
                    level.addParticle(
                        particleType,
                        rainParticlePosition.getX() + blockX,
                        rainParticlePosition.getY() + particleY,
                        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((float)cameraPosition.getY())) {
                    level.playLocalSound(rainParticlePosition, SoundEvents.WEATHER_RAIN_ABOVE, SoundSource.WEATHER, 0.1F, 0.5F, false);
                } else {
                    level.playLocalSound(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;
        } else {
            Biome biome = level.getBiome(pos).value();
            return biome.getPrecipitationAt(pos, level.getSeaLevel());
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    public record ColumnInstance(int x, int z, int bottomY, int topY, float uOffset, float vOffset, int lightCoords) {
    }
}

引用的其他类

  • RenderPass

    • 引用位置: 参数
  • RenderSystem

    • 引用位置: 方法调用
    • 关联成员: RenderSystem.bindDefaultUniforms(), RenderSystem.getDevice(), RenderSystem.getDynamicUniforms(), RenderSystem.getModelViewMatrix(), RenderSystem.getSamplerCache(), RenderSystem.getSequentialBuffer()
  • BufferBuilder

    • 引用位置: 构造调用
    • 关联成员: BufferBuilder()
  • ByteBufferBuilder

    • 引用位置: 方法调用
    • 关联成员: ByteBufferBuilder.exactlySized()
  • VertexConsumer

    • 引用位置: 参数
  • Camera

    • 引用位置: 参数
  • Minecraft

    • 引用位置: 方法调用
    • 关联成员: Minecraft.getInstance(), Minecraft.useShaderTransparency()
  • ClientLevel

    • 引用位置: 参数
  • LevelRenderer

    • 引用位置: 方法调用
    • 关联成员: LevelRenderer.getLightCoords()
  • WeatherRenderState

    • 引用位置: 参数
  • AbstractTexture

    • 引用位置: 参数
  • BlockPos

    • 引用位置: 参数/方法调用/构造调用
    • 关联成员: BlockPos.MutableBlockPos(), BlockPos.containing(), MutableBlockPos()
  • SectionPos

    • 引用位置: 方法调用
    • 关联成员: SectionPos.blockToSectionCoord()
  • Identifier

    • 引用位置: 字段/方法调用
    • 关联成员: Identifier.withDefaultNamespace()
  • ParticleStatus

    • 引用位置: 参数
  • ARGB

    • 引用位置: 方法调用
    • 关联成员: ARGB.white()
  • LightCoordsUtil

    • 引用位置: 方法调用
    • 关联成员: LightCoordsUtil.block(), LightCoordsUtil.pack(), LightCoordsUtil.sky()
  • Mth

    • 引用位置: 方法调用
    • 关联成员: Mth.floor(), Mth.length(), Mth.lengthSquared(), Mth.lerp()
  • RandomSource

    • 引用位置: 参数/方法调用
    • 关联成员: RandomSource.createThreadLocalInstance()
  • Level

    • 引用位置: 参数
  • Biome

    • 引用位置: 返回值
  • CampfireBlock

    • 引用位置: 方法调用
    • 关联成员: CampfireBlock.isLitCampfire()
  • Vec3

    • 引用位置: 参数