/* * Decompiled with CFR 0.152. */ package net.minecraft.client.renderer.block; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.renderer.BiomeColors; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.block.BlockModelShaper; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.MaterialSet; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.tags.FluidTags; import net.minecraft.util.Mth; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.HalfTransparentBlock; import net.minecraft.world.level.block.LeavesBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; public class LiquidBlockRenderer { private static final float MAX_FLUID_HEIGHT = 0.8888889f; private final TextureAtlasSprite[] lavaIcons = new TextureAtlasSprite[2]; private final TextureAtlasSprite[] waterIcons = new TextureAtlasSprite[2]; private TextureAtlasSprite waterOverlay; protected void setupSprites(BlockModelShaper blockModelShaper, MaterialSet materials) { this.lavaIcons[0] = blockModelShaper.getBlockModel(Blocks.LAVA.defaultBlockState()).particleIcon(); this.lavaIcons[1] = materials.get(ModelBakery.LAVA_FLOW); this.waterIcons[0] = blockModelShaper.getBlockModel(Blocks.WATER.defaultBlockState()).particleIcon(); this.waterIcons[1] = materials.get(ModelBakery.WATER_FLOW); this.waterOverlay = materials.get(ModelBakery.WATER_OVERLAY); } private static boolean isNeighborSameFluid(FluidState fluidState, FluidState neighborFluidState) { return neighborFluidState.getType().isSame(fluidState.getType()); } private static boolean isFaceOccludedByState(Direction direction, float height, BlockState state) { VoxelShape occluder = state.getFaceOcclusionShape(direction.getOpposite()); if (occluder == Shapes.empty()) { return false; } if (occluder == Shapes.block()) { boolean fullBlock = height == 1.0f; return direction != Direction.UP || fullBlock; } VoxelShape shape = Shapes.box(0.0, 0.0, 0.0, 1.0, height, 1.0); return Shapes.blockOccludes(shape, occluder, direction); } private static boolean isFaceOccludedByNeighbor(Direction direction, float height, BlockState neighborState) { return LiquidBlockRenderer.isFaceOccludedByState(direction, height, neighborState); } private static boolean isFaceOccludedBySelf(BlockState state, Direction direction) { return LiquidBlockRenderer.isFaceOccludedByState(direction.getOpposite(), 1.0f, state); } public static boolean shouldRenderFace(FluidState fluidState, BlockState blockState, Direction direction, FluidState neighborFluidState) { return !LiquidBlockRenderer.isFaceOccludedBySelf(blockState, direction) && !LiquidBlockRenderer.isNeighborSameFluid(fluidState, neighborFluidState); } public void tesselate(BlockAndTintGetter level, BlockPos pos, VertexConsumer builder, BlockState blockState, FluidState fluidState) { float bottomOffs; float heightSouthWest; float heightSouthEast; float heightNorthWest; float heightNorthEast; boolean isLava = fluidState.is(FluidTags.LAVA); TextureAtlasSprite[] sprites = isLava ? this.lavaIcons : this.waterIcons; int col = isLava ? 0xFFFFFF : BiomeColors.getAverageWaterColor(level, pos); float r = (float)(col >> 16 & 0xFF) / 255.0f; float g = (float)(col >> 8 & 0xFF) / 255.0f; float b = (float)(col & 0xFF) / 255.0f; BlockState blockStateDown = level.getBlockState(pos.relative(Direction.DOWN)); FluidState fluidStateDown = blockStateDown.getFluidState(); BlockState blockStateUp = level.getBlockState(pos.relative(Direction.UP)); FluidState fluidStateUp = blockStateUp.getFluidState(); BlockState blockStateNorth = level.getBlockState(pos.relative(Direction.NORTH)); FluidState fluidStateNorth = blockStateNorth.getFluidState(); BlockState blockStateSouth = level.getBlockState(pos.relative(Direction.SOUTH)); FluidState fluidStateSouth = blockStateSouth.getFluidState(); BlockState blockStateWest = level.getBlockState(pos.relative(Direction.WEST)); FluidState fluidStateWest = blockStateWest.getFluidState(); BlockState blockStateEast = level.getBlockState(pos.relative(Direction.EAST)); FluidState fluidStateEast = blockStateEast.getFluidState(); boolean renderUp = !LiquidBlockRenderer.isNeighborSameFluid(fluidState, fluidStateUp); boolean renderDown = LiquidBlockRenderer.shouldRenderFace(fluidState, blockState, Direction.DOWN, fluidStateDown) && !LiquidBlockRenderer.isFaceOccludedByNeighbor(Direction.DOWN, 0.8888889f, blockStateDown); boolean renderNorth = LiquidBlockRenderer.shouldRenderFace(fluidState, blockState, Direction.NORTH, fluidStateNorth); boolean renderSouth = LiquidBlockRenderer.shouldRenderFace(fluidState, blockState, Direction.SOUTH, fluidStateSouth); boolean renderWest = LiquidBlockRenderer.shouldRenderFace(fluidState, blockState, Direction.WEST, fluidStateWest); boolean renderEast = LiquidBlockRenderer.shouldRenderFace(fluidState, blockState, Direction.EAST, fluidStateEast); if (!(renderUp || renderDown || renderEast || renderWest || renderNorth || renderSouth)) { return; } float c10 = level.getShade(Direction.DOWN, true); float c11 = level.getShade(Direction.UP, true); float c2 = level.getShade(Direction.NORTH, true); float c3 = level.getShade(Direction.WEST, true); Fluid type = fluidState.getType(); float heightSelf = this.getHeight(level, type, pos, blockState, fluidState); if (heightSelf >= 1.0f) { heightNorthEast = 1.0f; heightNorthWest = 1.0f; heightSouthEast = 1.0f; heightSouthWest = 1.0f; } else { float heightNorth = this.getHeight(level, type, pos.north(), blockStateNorth, fluidStateNorth); float heightSouth = this.getHeight(level, type, pos.south(), blockStateSouth, fluidStateSouth); float heightEast = this.getHeight(level, type, pos.east(), blockStateEast, fluidStateEast); float heightWest = this.getHeight(level, type, pos.west(), blockStateWest, fluidStateWest); heightNorthEast = this.calculateAverageHeight(level, type, heightSelf, heightNorth, heightEast, pos.relative(Direction.NORTH).relative(Direction.EAST)); heightNorthWest = this.calculateAverageHeight(level, type, heightSelf, heightNorth, heightWest, pos.relative(Direction.NORTH).relative(Direction.WEST)); heightSouthEast = this.calculateAverageHeight(level, type, heightSelf, heightSouth, heightEast, pos.relative(Direction.SOUTH).relative(Direction.EAST)); heightSouthWest = this.calculateAverageHeight(level, type, heightSelf, heightSouth, heightWest, pos.relative(Direction.SOUTH).relative(Direction.WEST)); } float x = pos.getX() & 0xF; float y = pos.getY() & 0xF; float z = pos.getZ() & 0xF; float offs = 0.001f; float f = bottomOffs = renderDown ? 0.001f : 0.0f; if (renderUp && !LiquidBlockRenderer.isFaceOccludedByNeighbor(Direction.UP, Math.min(Math.min(heightNorthWest, heightSouthWest), Math.min(heightSouthEast, heightNorthEast)), blockStateUp)) { float v11; float u11; float v10; float u10; float v01; float u01; float v00; float u00; heightNorthWest -= 0.001f; heightSouthWest -= 0.001f; heightSouthEast -= 0.001f; heightNorthEast -= 0.001f; Vec3 flow = fluidState.getFlow(level, pos); if (flow.x == 0.0 && flow.z == 0.0) { sprite = sprites[0]; u00 = sprite.getU(0.0f); v00 = sprite.getV(0.0f); u01 = u00; v01 = sprite.getV(1.0f); u10 = sprite.getU(1.0f); v10 = v01; u11 = u10; v11 = v00; } else { sprite = sprites[1]; float angle = (float)Mth.atan2(flow.z, flow.x) - 1.5707964f; float s = Mth.sin(angle) * 0.25f; float c = Mth.cos(angle) * 0.25f; float cc = 0.5f; u00 = sprite.getU(0.5f + (-c - s)); v00 = sprite.getV(0.5f + (-c + s)); u01 = sprite.getU(0.5f + (-c + s)); v01 = sprite.getV(0.5f + (c + s)); u10 = sprite.getU(0.5f + (c + s)); v10 = sprite.getV(0.5f + (c - s)); u11 = sprite.getU(0.5f + (c - s)); v11 = sprite.getV(0.5f + (-c - s)); } int topColor = this.getLightColor(level, pos); float topRed = c11 * r; float topGreen = c11 * g; float topBlue = c11 * b; this.vertex(builder, x + 0.0f, y + heightNorthWest, z + 0.0f, topRed, topGreen, topBlue, u00, v00, topColor); this.vertex(builder, x + 0.0f, y + heightSouthWest, z + 1.0f, topRed, topGreen, topBlue, u01, v01, topColor); this.vertex(builder, x + 1.0f, y + heightSouthEast, z + 1.0f, topRed, topGreen, topBlue, u10, v10, topColor); this.vertex(builder, x + 1.0f, y + heightNorthEast, z + 0.0f, topRed, topGreen, topBlue, u11, v11, topColor); if (fluidState.shouldRenderBackwardUpFace(level, pos.above())) { this.vertex(builder, x + 0.0f, y + heightNorthWest, z + 0.0f, topRed, topGreen, topBlue, u00, v00, topColor); this.vertex(builder, x + 1.0f, y + heightNorthEast, z + 0.0f, topRed, topGreen, topBlue, u11, v11, topColor); this.vertex(builder, x + 1.0f, y + heightSouthEast, z + 1.0f, topRed, topGreen, topBlue, u10, v10, topColor); this.vertex(builder, x + 0.0f, y + heightSouthWest, z + 1.0f, topRed, topGreen, topBlue, u01, v01, topColor); } } if (renderDown) { float u0 = sprites[0].getU0(); float u1 = sprites[0].getU1(); float v0 = sprites[0].getV0(); float v1 = sprites[0].getV1(); int belowColor = this.getLightColor(level, pos.below()); float belowRed = c10 * r; float belowGreen = c10 * g; float belowBlue = c10 * b; this.vertex(builder, x, y + bottomOffs, z + 1.0f, belowRed, belowGreen, belowBlue, u0, v1, belowColor); this.vertex(builder, x, y + bottomOffs, z, belowRed, belowGreen, belowBlue, u0, v0, belowColor); this.vertex(builder, x + 1.0f, y + bottomOffs, z, belowRed, belowGreen, belowBlue, u1, v0, belowColor); this.vertex(builder, x + 1.0f, y + bottomOffs, z + 1.0f, belowRed, belowGreen, belowBlue, u1, v1, belowColor); } int sideColor = this.getLightColor(level, pos); for (Direction faceDir : Direction.Plane.HORIZONTAL) { Block relativeBlock; float z1; float z0; float x1; float x0; float hh1; float hh0; if (!(switch (faceDir) { case Direction.NORTH -> { hh0 = heightNorthWest; hh1 = heightNorthEast; x0 = x; x1 = x + 1.0f; z0 = z + 0.001f; z1 = z + 0.001f; yield renderNorth; } case Direction.SOUTH -> { hh0 = heightSouthEast; hh1 = heightSouthWest; x0 = x + 1.0f; x1 = x; z0 = z + 1.0f - 0.001f; z1 = z + 1.0f - 0.001f; yield renderSouth; } case Direction.WEST -> { hh0 = heightSouthWest; hh1 = heightNorthWest; x0 = x + 0.001f; x1 = x + 0.001f; z0 = z + 1.0f; z1 = z; yield renderWest; } default -> { hh0 = heightNorthEast; hh1 = heightSouthEast; x0 = x + 1.0f - 0.001f; x1 = x + 1.0f - 0.001f; z0 = z; z1 = z + 1.0f; yield renderEast; } }) || LiquidBlockRenderer.isFaceOccludedByNeighbor(faceDir, Math.max(hh0, hh1), level.getBlockState(pos.relative(faceDir)))) continue; BlockPos tPos = pos.relative(faceDir); TextureAtlasSprite sprite = sprites[1]; if (!isLava && ((relativeBlock = level.getBlockState(tPos).getBlock()) instanceof HalfTransparentBlock || relativeBlock instanceof LeavesBlock)) { sprite = this.waterOverlay; } float u0 = sprite.getU(0.0f); float u1 = sprite.getU(0.5f); float v01 = sprite.getV((1.0f - hh0) * 0.5f); float v02 = sprite.getV((1.0f - hh1) * 0.5f); float v1 = sprite.getV(0.5f); float br = faceDir.getAxis() == Direction.Axis.Z ? c2 : c3; float red = c11 * br * r; float green = c11 * br * g; float blue = c11 * br * b; this.vertex(builder, x0, y + hh0, z0, red, green, blue, u0, v01, sideColor); this.vertex(builder, x1, y + hh1, z1, red, green, blue, u1, v02, sideColor); this.vertex(builder, x1, y + bottomOffs, z1, red, green, blue, u1, v1, sideColor); this.vertex(builder, x0, y + bottomOffs, z0, red, green, blue, u0, v1, sideColor); if (sprite == this.waterOverlay) continue; this.vertex(builder, x0, y + bottomOffs, z0, red, green, blue, u0, v1, sideColor); this.vertex(builder, x1, y + bottomOffs, z1, red, green, blue, u1, v1, sideColor); this.vertex(builder, x1, y + hh1, z1, red, green, blue, u1, v02, sideColor); this.vertex(builder, x0, y + hh0, z0, red, green, blue, u0, v01, sideColor); } } private float calculateAverageHeight(BlockAndTintGetter level, Fluid type, float heightSelf, float height2, float height1, BlockPos cornerPos) { if (height1 >= 1.0f || height2 >= 1.0f) { return 1.0f; } float[] weightedHeight = new float[2]; if (height1 > 0.0f || height2 > 0.0f) { float heightCorner = this.getHeight(level, type, cornerPos); if (heightCorner >= 1.0f) { return 1.0f; } this.addWeightedHeight(weightedHeight, heightCorner); } this.addWeightedHeight(weightedHeight, heightSelf); this.addWeightedHeight(weightedHeight, height1); this.addWeightedHeight(weightedHeight, height2); return weightedHeight[0] / weightedHeight[1]; } private void addWeightedHeight(float[] weightedHeight, float height) { if (height >= 0.8f) { weightedHeight[0] = weightedHeight[0] + height * 10.0f; weightedHeight[1] = weightedHeight[1] + 10.0f; } else if (height >= 0.0f) { weightedHeight[0] = weightedHeight[0] + height; weightedHeight[1] = weightedHeight[1] + 1.0f; } } private float getHeight(BlockAndTintGetter level, Fluid fluidType, BlockPos pos) { BlockState state = level.getBlockState(pos); return this.getHeight(level, fluidType, pos, state, state.getFluidState()); } private float getHeight(BlockAndTintGetter level, Fluid fluidType, BlockPos pos, BlockState state, FluidState fluidState) { if (fluidType.isSame(fluidState.getType())) { BlockState aboveState = level.getBlockState(pos.above()); if (fluidType.isSame(aboveState.getFluidState().getType())) { return 1.0f; } return fluidState.getOwnHeight(); } if (!state.isSolid()) { return 0.0f; } return -1.0f; } private void vertex(VertexConsumer builder, float x, float y, float z, float red, float green, float blue, float u, float v, int light) { builder.addVertex(x, y, z).setColor(red, green, blue, 1.0f).setUv(u, v).setLight(light).setNormal(0.0f, 1.0f, 0.0f); } private int getLightColor(BlockAndTintGetter level, BlockPos pos) { int a = LevelRenderer.getLightColor(level, pos); int b = LevelRenderer.getLightColor(level, pos.above()); int aa = a & 0xFF; int ba = b & 0xFF; int ab = a >> 16 & 0xFF; int bb = b >> 16 & 0xFF; return (aa > ba ? aa : ba) | (ab > bb ? ab : bb) << 16; } }