FluidRenderer.java

net.minecraft.client.renderer.block.FluidRenderer

信息

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

    TODO

字段/常量

  • MAX_FLUID_HEIGHT

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

      TODO

  • fluidModels

    • 类型: FluidStateModelSet
    • 修饰符: private final
    • 源码定位: L29
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.renderer.block.FluidRenderer.Output
    • 类型: interface
    • 修饰符: public
    • 源码定位: L431
    • 说明:

      TODO

构造器

public FluidRenderer(FluidStateModelSet fluidModels) @ L31

  • 构造器名:FluidRenderer
  • 源码定位:L31
  • 修饰符:public

参数:

  • fluidModels: FluidStateModelSet

说明:

TODO

方法

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

private static boolean isNeighborSameFluid(FluidState fluidState, FluidState neighborFluidState) @ L35

  • 方法名:isNeighborSameFluid
  • 源码定位:L35
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • fluidState: FluidState
  • neighborFluidState: FluidState

说明:

TODO

private static boolean isFaceOccludedByState(Direction direction, float height, BlockState state) @ L39

  • 方法名:isFaceOccludedByState
  • 源码定位:L39
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • direction: Direction
  • height: float
  • state: BlockState

说明:

TODO

private static boolean isFaceOccludedByNeighbor(Direction direction, float height, BlockState neighborState) @ L52

  • 方法名:isFaceOccludedByNeighbor
  • 源码定位:L52
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • direction: Direction
  • height: float
  • neighborState: BlockState

说明:

TODO

private static boolean isFaceOccludedBySelf(BlockState state, Direction direction) @ L56

  • 方法名:isFaceOccludedBySelf
  • 源码定位:L56
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • state: BlockState
  • direction: Direction

说明:

TODO

public static boolean shouldRenderFace(FluidState fluidState, BlockState blockState, Direction direction, FluidState neighborFluidState) @ L60

  • 方法名:shouldRenderFace
  • 源码定位:L60
  • 返回类型:boolean
  • 修饰符:public static

参数:

  • fluidState: FluidState
  • blockState: BlockState
  • direction: Direction
  • neighborFluidState: FluidState

说明:

TODO

public void tesselate(BlockAndTintGetter level, BlockPos pos, FluidRenderer.Output output, BlockState blockState, FluidState fluidState) @ L64

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

参数:

  • level: BlockAndTintGetter
  • pos: BlockPos
  • output: FluidRenderer.Output
  • blockState: BlockState
  • fluidState: FluidState

说明:

TODO

private void addFace(VertexConsumer builder, float x0, float y0, float z0, float u0, float v0, float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, int color, int lightCoords, boolean addBackFace) @ L338

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

参数:

  • builder: VertexConsumer
  • x0: float
  • y0: float
  • z0: float
  • u0: float
  • v0: float
  • x1: float
  • y1: float
  • z1: float
  • u1: float
  • v1: float
  • x2: float
  • y2: float
  • z2: float
  • u2: float
  • v2: float
  • x3: float
  • y3: float
  • z3: float
  • u3: float
  • v3: float
  • color: int
  • lightCoords: int
  • addBackFace: boolean

说明:

TODO

private float calculateAverageHeight(BlockAndTintGetter level, Fluid type, float heightSelf, float height2, float height1, BlockPos cornerPos) @ L376

  • 方法名:calculateAverageHeight
  • 源码定位:L376
  • 返回类型:float
  • 修饰符:private

参数:

  • level: BlockAndTintGetter
  • type: Fluid
  • heightSelf: float
  • height2: float
  • height1: float
  • cornerPos: BlockPos

说明:

TODO

private void addWeightedHeight(float[] weightedHeight, float height) @ L397

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

参数:

  • weightedHeight: float[]
  • height: float

说明:

TODO

private float getHeight(BlockAndTintGetter level, Fluid fluidType, BlockPos pos) @ L407

  • 方法名:getHeight
  • 源码定位:L407
  • 返回类型:float
  • 修饰符:private

参数:

  • level: BlockAndTintGetter
  • fluidType: Fluid
  • pos: BlockPos

说明:

TODO

private float getHeight(BlockAndTintGetter level, Fluid fluidType, BlockPos pos, BlockState state, FluidState fluidState) @ L412

  • 方法名:getHeight
  • 源码定位:L412
  • 返回类型:float
  • 修饰符:private

参数:

  • level: BlockAndTintGetter
  • fluidType: Fluid
  • pos: BlockPos
  • state: BlockState
  • fluidState: FluidState

说明:

TODO

private void vertex(VertexConsumer builder, float x, float y, float z, int color, float u, float v, int lightCoords) @ L421

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

参数:

  • builder: VertexConsumer
  • x: float
  • y: float
  • z: float
  • color: int
  • u: float
  • v: float
  • lightCoords: int

说明:

TODO

private int getLightCoords(BlockAndTintGetter level, BlockPos pos) @ L425

  • 方法名:getLightCoords
  • 源码定位:L425
  • 返回类型:int
  • 修饰符:private

参数:

  • level: BlockAndTintGetter
  • pos: BlockPos

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class FluidRenderer {
    private static final float MAX_FLUID_HEIGHT = 0.8888889F;
    private final FluidStateModelSet fluidModels;
 
    public FluidRenderer(FluidStateModelSet fluidModels) {
        this.fluidModels = fluidModels;
    }
 
    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;
        } else if (occluder == Shapes.block()) {
            boolean fullBlock = height == 1.0F;
            return direction != Direction.UP || fullBlock;
        } else {
            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 isFaceOccludedByState(direction, height, neighborState);
    }
 
    private static boolean isFaceOccludedBySelf(BlockState state, Direction direction) {
        return isFaceOccludedByState(direction.getOpposite(), 1.0F, state);
    }
 
    public static boolean shouldRenderFace(FluidState fluidState, BlockState blockState, Direction direction, FluidState neighborFluidState) {
        return !isNeighborSameFluid(fluidState, neighborFluidState) && !isFaceOccludedBySelf(blockState, direction);
    }
 
    public void tesselate(BlockAndTintGetter level, BlockPos pos, FluidRenderer.Output output, BlockState blockState, FluidState fluidState) {
        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 = !isNeighborSameFluid(fluidState, fluidStateUp);
        boolean renderDown = shouldRenderFace(fluidState, blockState, Direction.DOWN, fluidStateDown)
            && !isFaceOccludedByNeighbor(Direction.DOWN, 0.8888889F, blockStateDown);
        boolean renderNorth = shouldRenderFace(fluidState, blockState, Direction.NORTH, fluidStateNorth);
        boolean renderSouth = shouldRenderFace(fluidState, blockState, Direction.SOUTH, fluidStateSouth);
        boolean renderWest = shouldRenderFace(fluidState, blockState, Direction.WEST, fluidStateWest);
        boolean renderEast = shouldRenderFace(fluidState, blockState, Direction.EAST, fluidStateEast);
        if (renderUp || renderDown || renderEast || renderWest || renderNorth || renderSouth) {
            FluidModel model = this.fluidModels.get(fluidState);
            VertexConsumer builder = output.getBuilder(model.layer());
            int tintColor = model.tintSource() != null ? model.tintSource().colorInWorld(blockState, level, pos) : -1;
            CardinalLighting cardinalLighting = level.cardinalLighting();
            Fluid type = fluidState.getType();
            float heightSelf = this.getHeight(level, type, pos, blockState, fluidState);
            float heightNorthEast;
            float heightNorthWest;
            float heightSouthEast;
            float heightSouthWest;
            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() & 15;
            float y = pos.getY() & 15;
            float z = pos.getZ() & 15;
            float offs = 0.001F;
            float bottomOffs = renderDown ? 0.001F : 0.0F;
            if (renderUp
                && !isFaceOccludedByNeighbor(
                    Direction.UP, Math.min(Math.min(heightNorthWest, heightSouthWest), Math.min(heightSouthEast, heightNorthEast)), blockStateUp
                )) {
                heightNorthWest -= 0.001F;
                heightSouthWest -= 0.001F;
                heightSouthEast -= 0.001F;
                heightNorthEast -= 0.001F;
                Vec3 flow = fluidState.getFlow(level, pos);
                float u00;
                float u01;
                float u10;
                float u11;
                float v00;
                float v01;
                float v10;
                float v11;
                if (flow.x == 0.0 && flow.z == 0.0) {
                    TextureAtlasSprite stillSprite = model.stillMaterial().sprite();
                    u00 = stillSprite.getU0();
                    v00 = stillSprite.getV0();
                    u01 = u00;
                    v01 = stillSprite.getV1();
                    u10 = stillSprite.getU1();
                    v10 = v01;
                    u11 = u10;
                    v11 = v00;
                } else {
                    float angle = (float)Mth.atan2(flow.z, flow.x) - (float) (Math.PI / 2);
                    float s = Mth.sin(angle) * 0.25F;
                    float c = Mth.cos(angle) * 0.25F;
                    float cc = 0.5F;
                    TextureAtlasSprite flowingSprite = model.flowingMaterial().sprite();
                    u00 = flowingSprite.getU(0.5F + (-c - s));
                    v00 = flowingSprite.getV(0.5F + (-c + s));
                    u01 = flowingSprite.getU(0.5F + (-c + s));
                    v01 = flowingSprite.getV(0.5F + (c + s));
                    u10 = flowingSprite.getU(0.5F + (c + s));
                    v10 = flowingSprite.getV(0.5F + (c - s));
                    u11 = flowingSprite.getU(0.5F + (c - s));
                    v11 = flowingSprite.getV(0.5F + (-c - s));
                }
 
                int topLightCoords = this.getLightCoords(level, pos);
                int topColor = ARGB.scaleRGB(tintColor, cardinalLighting.up());
                this.addFace(
                    builder,
                    x + 0.0F,
                    y + heightNorthWest,
                    z + 0.0F,
                    u00,
                    v00,
                    x + 0.0F,
                    y + heightSouthWest,
                    z + 1.0F,
                    u01,
                    v01,
                    x + 1.0F,
                    y + heightSouthEast,
                    z + 1.0F,
                    u10,
                    v10,
                    x + 1.0F,
                    y + heightNorthEast,
                    z + 0.0F,
                    u11,
                    v11,
                    topColor,
                    topLightCoords,
                    fluidState.shouldRenderBackwardUpFace(level, pos.above())
                );
            }
 
            if (renderDown) {
                TextureAtlasSprite stillSprite = model.stillMaterial().sprite();
                float u0 = stillSprite.getU0();
                float u1 = stillSprite.getU1();
                float v0 = stillSprite.getV0();
                float v1 = stillSprite.getV1();
                int belowLightCoords = this.getLightCoords(level, pos.below());
                int belowColor = ARGB.scaleRGB(tintColor, cardinalLighting.down());
                this.addFace(
                    builder,
                    x,
                    y + bottomOffs,
                    z,
                    u0,
                    v0,
                    x + 1.0F,
                    y + bottomOffs,
                    z,
                    u1,
                    v0,
                    x + 1.0F,
                    y + bottomOffs,
                    z + 1.0F,
                    u1,
                    v1,
                    x,
                    y + bottomOffs,
                    z + 1.0F,
                    u0,
                    v1,
                    belowColor,
                    belowLightCoords,
                    false
                );
            }
 
            int sideLightCoords = this.getLightCoords(level, pos);
 
            for (Direction faceDir : Direction.Plane.HORIZONTAL) {
                float hh0;
                float hh1;
                float x0;
                float z0;
                float x1;
                float z1;
                boolean renderCondition;
                BlockState faceState;
                switch (faceDir) {
                    case NORTH:
                        hh0 = heightNorthWest;
                        hh1 = heightNorthEast;
                        x0 = x;
                        x1 = x + 1.0F;
                        z0 = z + 0.001F;
                        z1 = z + 0.001F;
                        renderCondition = renderNorth;
                        faceState = blockStateNorth;
                        break;
                    case SOUTH:
                        hh0 = heightSouthEast;
                        hh1 = heightSouthWest;
                        x0 = x + 1.0F;
                        x1 = x;
                        z0 = z + 1.0F - 0.001F;
                        z1 = z + 1.0F - 0.001F;
                        renderCondition = renderSouth;
                        faceState = blockStateSouth;
                        break;
                    case WEST:
                        hh0 = heightSouthWest;
                        hh1 = heightNorthWest;
                        x0 = x + 0.001F;
                        x1 = x + 0.001F;
                        z0 = z + 1.0F;
                        z1 = z;
                        renderCondition = renderWest;
                        faceState = blockStateWest;
                        break;
                    case EAST:
                        hh0 = heightNorthEast;
                        hh1 = heightSouthEast;
                        x0 = x + 1.0F - 0.001F;
                        x1 = x + 1.0F - 0.001F;
                        z0 = z;
                        z1 = z + 1.0F;
                        renderCondition = renderEast;
                        faceState = blockStateEast;
                        break;
                    default:
                        throw new UnsupportedOperationException();
                }
 
                if (renderCondition && !isFaceOccludedByNeighbor(faceDir, Math.max(hh0, hh1), faceState)) {
                    TextureAtlasSprite sprite = model.flowingMaterial().sprite();
                    boolean isOverlay = false;
                    if (model.overlayMaterial() != null) {
                        Block relativeBlock = faceState.getBlock();
                        if (relativeBlock instanceof HalfTransparentBlock || relativeBlock instanceof LeavesBlock) {
                            sprite = model.overlayMaterial().sprite();
                            isOverlay = true;
                        }
                    }
 
                    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 shadeSide = faceDir.getAxis() == Direction.Axis.Z ? cardinalLighting.north() : cardinalLighting.west();
                    int faceColor = ARGB.scaleRGB(tintColor, cardinalLighting.up() * shadeSide);
                    this.addFace(
                        builder,
                        x0,
                        y + hh0,
                        z0,
                        u0,
                        v01,
                        x1,
                        y + hh1,
                        z1,
                        u1,
                        v02,
                        x1,
                        y + bottomOffs,
                        z1,
                        u1,
                        v1,
                        x0,
                        y + bottomOffs,
                        z0,
                        u0,
                        v1,
                        faceColor,
                        sideLightCoords,
                        !isOverlay
                    );
                }
            }
        }
    }
 
    private void addFace(
        VertexConsumer builder,
        float x0,
        float y0,
        float z0,
        float u0,
        float v0,
        float x1,
        float y1,
        float z1,
        float u1,
        float v1,
        float x2,
        float y2,
        float z2,
        float u2,
        float v2,
        float x3,
        float y3,
        float z3,
        float u3,
        float v3,
        int color,
        int lightCoords,
        boolean addBackFace
    ) {
        this.vertex(builder, x0, y0, z0, color, u0, v0, lightCoords);
        this.vertex(builder, x1, y1, z1, color, u1, v1, lightCoords);
        this.vertex(builder, x2, y2, z2, color, u2, v2, lightCoords);
        this.vertex(builder, x3, y3, z3, color, u3, v3, lightCoords);
        if (addBackFace) {
            this.vertex(builder, x3, y3, z3, color, u3, v3, lightCoords);
            this.vertex(builder, x2, y2, z2, color, u2, v2, lightCoords);
            this.vertex(builder, x1, y1, z1, color, u1, v1, lightCoords);
            this.vertex(builder, x0, y0, z0, color, u0, v0, lightCoords);
        }
    }
 
    private float calculateAverageHeight(BlockAndTintGetter level, Fluid type, float heightSelf, float height2, float height1, BlockPos cornerPos) {
        if (!(height1 >= 1.0F) && !(height2 >= 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];
        } else {
            return 1.0F;
        }
    }
 
    private void addWeightedHeight(float[] weightedHeight, float height) {
        if (height >= 0.8F) {
            weightedHeight[0] += height * 10.0F;
            weightedHeight[1] += 10.0F;
        } else if (height >= 0.0F) {
            weightedHeight[0] += height;
            weightedHeight[1]++;
        }
    }
 
    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());
            return fluidType.isSame(aboveState.getFluidState().getType()) ? 1.0F : fluidState.getOwnHeight();
        } else {
            return !state.isSolid() ? 0.0F : -1.0F;
        }
    }
 
    private void vertex(VertexConsumer builder, float x, float y, float z, int color, float u, float v, int lightCoords) {
        builder.addVertex(x, y, z, color, u, v, OverlayTexture.NO_OVERLAY, lightCoords, 0.0F, 1.0F, 0.0F);
    }
 
    private int getLightCoords(BlockAndTintGetter level, BlockPos pos) {
        return LightCoordsUtil.max(LevelRenderer.getLightCoords(level, pos), LevelRenderer.getLightCoords(level, pos.above()));
    }
 
    @FunctionalInterface
    @OnlyIn(Dist.CLIENT)
    public interface Output {
        VertexConsumer getBuilder(ChunkSectionLayer layer);
    }
}

引用的其他类

  • VertexConsumer

    • 引用位置: 参数
  • LevelRenderer

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

    • 引用位置: 参数
  • FluidStateModelSet

    • 引用位置: 参数/字段
  • BlockPos

    • 引用位置: 参数
  • Direction

    • 引用位置: 参数
  • ARGB

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

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

    • 引用位置: 方法调用
    • 关联成员: Mth.atan2(), Mth.cos(), Mth.sin()
  • BlockState

    • 引用位置: 参数
  • Fluid

    • 引用位置: 参数
  • FluidState

    • 引用位置: 参数
  • Shapes

    • 引用位置: 方法调用
    • 关联成员: Shapes.block(), Shapes.blockOccludes(), Shapes.box(), Shapes.empty()