minecraft-src/net/minecraft/client/renderer/block/LiquidBlockRenderer.java
2025-07-04 03:45:38 +03:00

350 lines
14 KiB
Java

package net.minecraft.client.renderer.block;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
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;
@Environment(EnvType.CLIENT)
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() {
this.lavaIcons[0] = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(Blocks.LAVA.defaultBlockState()).particleIcon();
this.lavaIcons[1] = ModelBakery.LAVA_FLOW.sprite();
this.waterIcons[0] = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(Blocks.WATER.defaultBlockState()).particleIcon();
this.waterIcons[1] = ModelBakery.WATER_FLOW.sprite();
this.waterOverlay = ModelBakery.WATER_OVERLAY.sprite();
}
private static boolean isNeighborSameFluid(FluidState firstState, FluidState secondState) {
return secondState.getType().isSame(firstState.getType());
}
private static boolean isFaceOccludedByState(Direction face, float height, BlockState state) {
VoxelShape voxelShape = state.getFaceOcclusionShape(face.getOpposite());
if (voxelShape == Shapes.empty()) {
return false;
} else if (voxelShape == Shapes.block()) {
boolean bl = height == 1.0F;
return face != Direction.UP || bl;
} else {
VoxelShape voxelShape2 = Shapes.box(0.0, 0.0, 0.0, 1.0, height, 1.0);
return Shapes.blockOccludes(voxelShape2, voxelShape, face);
}
}
private static boolean isFaceOccludedByNeighbor(Direction face, float height, BlockState state) {
return isFaceOccludedByState(face, height, state);
}
private static boolean isFaceOccludedBySelf(BlockState state, Direction face) {
return isFaceOccludedByState(face.getOpposite(), 1.0F, state);
}
public static boolean shouldRenderFace(FluidState fluidState, BlockState blockState, Direction side, FluidState neighborFluid) {
return !isFaceOccludedBySelf(blockState, side) && !isNeighborSameFluid(fluidState, neighborFluid);
}
public void tesselate(BlockAndTintGetter level, BlockPos pos, VertexConsumer buffer, BlockState blockState, FluidState fluidState) {
boolean bl = fluidState.is(FluidTags.LAVA);
TextureAtlasSprite[] textureAtlasSprites = bl ? this.lavaIcons : this.waterIcons;
int i = bl ? 16777215 : BiomeColors.getAverageWaterColor(level, pos);
float f = (i >> 16 & 0xFF) / 255.0F;
float g = (i >> 8 & 0xFF) / 255.0F;
float h = (i & 0xFF) / 255.0F;
BlockState blockState2 = level.getBlockState(pos.relative(Direction.DOWN));
FluidState fluidState2 = blockState2.getFluidState();
BlockState blockState3 = level.getBlockState(pos.relative(Direction.UP));
FluidState fluidState3 = blockState3.getFluidState();
BlockState blockState4 = level.getBlockState(pos.relative(Direction.NORTH));
FluidState fluidState4 = blockState4.getFluidState();
BlockState blockState5 = level.getBlockState(pos.relative(Direction.SOUTH));
FluidState fluidState5 = blockState5.getFluidState();
BlockState blockState6 = level.getBlockState(pos.relative(Direction.WEST));
FluidState fluidState6 = blockState6.getFluidState();
BlockState blockState7 = level.getBlockState(pos.relative(Direction.EAST));
FluidState fluidState7 = blockState7.getFluidState();
boolean bl2 = !isNeighborSameFluid(fluidState, fluidState3);
boolean bl3 = shouldRenderFace(fluidState, blockState, Direction.DOWN, fluidState2) && !isFaceOccludedByNeighbor(Direction.DOWN, 0.8888889F, blockState2);
boolean bl4 = shouldRenderFace(fluidState, blockState, Direction.NORTH, fluidState4);
boolean bl5 = shouldRenderFace(fluidState, blockState, Direction.SOUTH, fluidState5);
boolean bl6 = shouldRenderFace(fluidState, blockState, Direction.WEST, fluidState6);
boolean bl7 = shouldRenderFace(fluidState, blockState, Direction.EAST, fluidState7);
if (bl2 || bl3 || bl7 || bl6 || bl4 || bl5) {
float j = level.getShade(Direction.DOWN, true);
float k = level.getShade(Direction.UP, true);
float l = level.getShade(Direction.NORTH, true);
float m = level.getShade(Direction.WEST, true);
Fluid fluid = fluidState.getType();
float n = this.getHeight(level, fluid, pos, blockState, fluidState);
float o;
float p;
float q;
float r;
if (n >= 1.0F) {
o = 1.0F;
p = 1.0F;
q = 1.0F;
r = 1.0F;
} else {
float s = this.getHeight(level, fluid, pos.north(), blockState4, fluidState4);
float t = this.getHeight(level, fluid, pos.south(), blockState5, fluidState5);
float u = this.getHeight(level, fluid, pos.east(), blockState7, fluidState7);
float v = this.getHeight(level, fluid, pos.west(), blockState6, fluidState6);
o = this.calculateAverageHeight(level, fluid, n, s, u, pos.relative(Direction.NORTH).relative(Direction.EAST));
p = this.calculateAverageHeight(level, fluid, n, s, v, pos.relative(Direction.NORTH).relative(Direction.WEST));
q = this.calculateAverageHeight(level, fluid, n, t, u, pos.relative(Direction.SOUTH).relative(Direction.EAST));
r = this.calculateAverageHeight(level, fluid, n, t, v, pos.relative(Direction.SOUTH).relative(Direction.WEST));
}
float s = pos.getX() & 15;
float t = pos.getY() & 15;
float u = pos.getZ() & 15;
float v = 0.001F;
float w = bl3 ? 0.001F : 0.0F;
if (bl2 && !isFaceOccludedByNeighbor(Direction.UP, Math.min(Math.min(p, r), Math.min(q, o)), blockState3)) {
p -= 0.001F;
r -= 0.001F;
q -= 0.001F;
o -= 0.001F;
Vec3 vec3 = fluidState.getFlow(level, pos);
float x;
float z;
float ab;
float ad;
float y;
float aa;
float ac;
float ae;
if (vec3.x == 0.0 && vec3.z == 0.0) {
TextureAtlasSprite textureAtlasSprite = textureAtlasSprites[0];
x = textureAtlasSprite.getU(0.0F);
y = textureAtlasSprite.getV(0.0F);
z = x;
aa = textureAtlasSprite.getV(1.0F);
ab = textureAtlasSprite.getU(1.0F);
ac = aa;
ad = ab;
ae = y;
} else {
TextureAtlasSprite textureAtlasSprite = textureAtlasSprites[1];
float af = (float)Mth.atan2(vec3.z, vec3.x) - (float) (Math.PI / 2);
float ag = Mth.sin(af) * 0.25F;
float ah = Mth.cos(af) * 0.25F;
float ai = 0.5F;
x = textureAtlasSprite.getU(0.5F + (-ah - ag));
y = textureAtlasSprite.getV(0.5F + (-ah + ag));
z = textureAtlasSprite.getU(0.5F + (-ah + ag));
aa = textureAtlasSprite.getV(0.5F + (ah + ag));
ab = textureAtlasSprite.getU(0.5F + (ah + ag));
ac = textureAtlasSprite.getV(0.5F + (ah - ag));
ad = textureAtlasSprite.getU(0.5F + (ah - ag));
ae = textureAtlasSprite.getV(0.5F + (-ah - ag));
}
float aj = (x + z + ab + ad) / 4.0F;
float af = (y + aa + ac + ae) / 4.0F;
float ag = textureAtlasSprites[0].uvShrinkRatio();
x = Mth.lerp(ag, x, aj);
z = Mth.lerp(ag, z, aj);
ab = Mth.lerp(ag, ab, aj);
ad = Mth.lerp(ag, ad, aj);
y = Mth.lerp(ag, y, af);
aa = Mth.lerp(ag, aa, af);
ac = Mth.lerp(ag, ac, af);
ae = Mth.lerp(ag, ae, af);
int ak = this.getLightColor(level, pos);
float ai = k * f;
float al = k * g;
float am = k * h;
this.vertex(buffer, s + 0.0F, t + p, u + 0.0F, ai, al, am, x, y, ak);
this.vertex(buffer, s + 0.0F, t + r, u + 1.0F, ai, al, am, z, aa, ak);
this.vertex(buffer, s + 1.0F, t + q, u + 1.0F, ai, al, am, ab, ac, ak);
this.vertex(buffer, s + 1.0F, t + o, u + 0.0F, ai, al, am, ad, ae, ak);
if (fluidState.shouldRenderBackwardUpFace(level, pos.above())) {
this.vertex(buffer, s + 0.0F, t + p, u + 0.0F, ai, al, am, x, y, ak);
this.vertex(buffer, s + 1.0F, t + o, u + 0.0F, ai, al, am, ad, ae, ak);
this.vertex(buffer, s + 1.0F, t + q, u + 1.0F, ai, al, am, ab, ac, ak);
this.vertex(buffer, s + 0.0F, t + r, u + 1.0F, ai, al, am, z, aa, ak);
}
}
if (bl3) {
float xx = textureAtlasSprites[0].getU0();
float zx = textureAtlasSprites[0].getU1();
float abx = textureAtlasSprites[0].getV0();
float adx = textureAtlasSprites[0].getV1();
int an = this.getLightColor(level, pos.below());
float aax = j * f;
float acx = j * g;
float aex = j * h;
this.vertex(buffer, s, t + w, u + 1.0F, aax, acx, aex, xx, adx, an);
this.vertex(buffer, s, t + w, u, aax, acx, aex, xx, abx, an);
this.vertex(buffer, s + 1.0F, t + w, u, aax, acx, aex, zx, abx, an);
this.vertex(buffer, s + 1.0F, t + w, u + 1.0F, aax, acx, aex, zx, adx, an);
}
int ao = this.getLightColor(level, pos);
for (Direction direction : Direction.Plane.HORIZONTAL) {
float adx;
float yx;
float aax;
float acx;
float aex;
float ap;
boolean bl8;
switch (direction) {
case NORTH:
adx = p;
yx = o;
aax = s;
aex = s + 1.0F;
acx = u + 0.001F;
ap = u + 0.001F;
bl8 = bl4;
break;
case SOUTH:
adx = q;
yx = r;
aax = s + 1.0F;
aex = s;
acx = u + 1.0F - 0.001F;
ap = u + 1.0F - 0.001F;
bl8 = bl5;
break;
case WEST:
adx = r;
yx = p;
aax = s + 0.001F;
aex = s + 0.001F;
acx = u + 1.0F;
ap = u;
bl8 = bl6;
break;
default:
adx = o;
yx = q;
aax = s + 1.0F - 0.001F;
aex = s + 1.0F - 0.001F;
acx = u;
ap = u + 1.0F;
bl8 = bl7;
}
if (bl8 && !isFaceOccludedByNeighbor(direction, Math.max(adx, yx), level.getBlockState(pos.relative(direction)))) {
BlockPos blockPos = pos.relative(direction);
TextureAtlasSprite textureAtlasSprite2 = textureAtlasSprites[1];
if (!bl) {
Block block = level.getBlockState(blockPos).getBlock();
if (block instanceof HalfTransparentBlock || block instanceof LeavesBlock) {
textureAtlasSprite2 = this.waterOverlay;
}
}
float ah = textureAtlasSprite2.getU(0.0F);
float ai = textureAtlasSprite2.getU(0.5F);
float al = textureAtlasSprite2.getV((1.0F - adx) * 0.5F);
float am = textureAtlasSprite2.getV((1.0F - yx) * 0.5F);
float aq = textureAtlasSprite2.getV(0.5F);
float ar = direction.getAxis() == Direction.Axis.Z ? l : m;
float as = k * ar * f;
float at = k * ar * g;
float au = k * ar * h;
this.vertex(buffer, aax, t + adx, acx, as, at, au, ah, al, ao);
this.vertex(buffer, aex, t + yx, ap, as, at, au, ai, am, ao);
this.vertex(buffer, aex, t + w, ap, as, at, au, ai, aq, ao);
this.vertex(buffer, aax, t + w, acx, as, at, au, ah, aq, ao);
if (textureAtlasSprite2 != this.waterOverlay) {
this.vertex(buffer, aax, t + w, acx, as, at, au, ah, aq, ao);
this.vertex(buffer, aex, t + w, ap, as, at, au, ai, aq, ao);
this.vertex(buffer, aex, t + yx, ap, as, at, au, ai, am, ao);
this.vertex(buffer, aax, t + adx, acx, as, at, au, ah, al, ao);
}
}
}
}
}
private float calculateAverageHeight(BlockAndTintGetter level, Fluid fluid, float currentHeight, float height1, float height2, BlockPos pos) {
if (!(height2 >= 1.0F) && !(height1 >= 1.0F)) {
float[] fs = new float[2];
if (height2 > 0.0F || height1 > 0.0F) {
float f = this.getHeight(level, fluid, pos);
if (f >= 1.0F) {
return 1.0F;
}
this.addWeightedHeight(fs, f);
}
this.addWeightedHeight(fs, currentHeight);
this.addWeightedHeight(fs, height2);
this.addWeightedHeight(fs, height1);
return fs[0] / fs[1];
} else {
return 1.0F;
}
}
private void addWeightedHeight(float[] output, float height) {
if (height >= 0.8F) {
output[0] += height * 10.0F;
output[1] += 10.0F;
} else if (height >= 0.0F) {
output[0] += height;
output[1]++;
}
}
private float getHeight(BlockAndTintGetter level, Fluid fluid, BlockPos pos) {
BlockState blockState = level.getBlockState(pos);
return this.getHeight(level, fluid, pos, blockState, blockState.getFluidState());
}
private float getHeight(BlockAndTintGetter level, Fluid fluid, BlockPos pos, BlockState blockState, FluidState fluidState) {
if (fluid.isSame(fluidState.getType())) {
BlockState blockState2 = level.getBlockState(pos.above());
return fluid.isSame(blockState2.getFluidState().getType()) ? 1.0F : fluidState.getOwnHeight();
} else {
return !blockState.isSolid() ? 0.0F : -1.0F;
}
}
private void vertex(VertexConsumer buffer, float x, float y, float z, float red, float green, float blue, float u, float v, int packedLight) {
buffer.addVertex(x, y, z).setColor(red, green, blue, 1.0F).setUv(u, v).setLight(packedLight).setNormal(0.0F, 1.0F, 0.0F);
}
private int getLightColor(BlockAndTintGetter level, BlockPos pos) {
int i = LevelRenderer.getLightColor(level, pos);
int j = LevelRenderer.getLightColor(level, pos.above());
int k = i & 0xFF;
int l = j & 0xFF;
int m = i >> 16 & 0xFF;
int n = j >> 16 & 0xFF;
return (k > l ? k : l) | (m > n ? m : n) << 16;
}
}