From d1afc78c9cea63dc5ae6e7fcc8cf0ab7e05df9eb Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 27 Nov 2024 04:13:27 -0500 Subject: [PATCH] [1.21.1] Add a hook for blocks to suppress a neighboring fluid overlay (#1701) Co-authored-by: Marc Hermans --- .../block/LiquidBlockRenderer.java.patch | 51 ++++++++++++- .../common/extensions/IBlockExtension.java | 12 ++++ .../extensions/IBlockStateExtension.java | 11 +++ .../blockstates/water_glass.json | 7 ++ .../lang/en_us.json | 3 + .../models/block/water_glass.json | 7 ++ .../debug/fluid/ClientFluidTests.java | 72 +++++++++++++++++++ 7 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json create mode 100644 tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json create mode 100644 tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json create mode 100644 tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java diff --git a/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch b/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch index 5eab04bb47..043e415274 100644 --- a/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/block/LiquidBlockRenderer.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/client/renderer/block/LiquidBlockRenderer.java +++ b/net/minecraft/client/renderer/block/LiquidBlockRenderer.java -@@ -38,6 +_,7 @@ +@@ -38,12 +_,17 @@ this.waterIcons[0] = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(Blocks.WATER.defaultBlockState()).getParticleIcon(); this.waterIcons[1] = ModelBakery.WATER_FLOW.sprite(); this.waterOverlay = ModelBakery.WATER_OVERLAY.sprite(); @@ -8,8 +8,35 @@ } private static boolean isNeighborSameFluid(FluidState p_203186_, FluidState p_203187_) { -@@ -70,8 +_,9 @@ + return p_203187_.getType().isSame(p_203186_.getType()); + } + ++ private static boolean isNeighborStateHidingOverlay(FluidState selfState, BlockState otherState, Direction neighborFace) { ++ return otherState.shouldHideAdjacentFluidFace(neighborFace, selfState); ++ } ++ + private static boolean isFaceOccludedByState(BlockGetter p_110979_, Direction p_110980_, float p_110981_, BlockPos p_110982_, BlockState p_110983_) { + if (p_110983_.canOcclude()) { + VoxelShape voxelshape = Shapes.box(0.0, 0.0, 0.0, 1.0, (double)p_110981_, 1.0); +@@ -62,16 +_,26 @@ + return isFaceOccludedByState(p_110960_, p_110963_.getOpposite(), 1.0F, p_110961_, p_110962_); + } + ++ /** @deprecated Neo: use overload that accepts BlockState */ + public static boolean shouldRenderFace( + BlockAndTintGetter p_203167_, BlockPos p_203168_, FluidState p_203169_, BlockState p_203170_, Direction p_203171_, FluidState p_203172_ + ) { + return !isFaceOccludedBySelf(p_203167_, p_203168_, p_203170_, p_203171_) && !isNeighborSameFluid(p_203169_, p_203172_); + } ++ public static boolean shouldRenderFace( ++ BlockAndTintGetter level, BlockPos pos, FluidState fluidState, BlockState selfState, Direction direction, BlockState otherState ++ ) { ++ return !isFaceOccludedBySelf(level, pos, selfState, direction) && !isNeighborStateHidingOverlay(fluidState, otherState, direction.getOpposite()); ++ } ++ ++ ++ public void tesselate(BlockAndTintGetter p_234370_, BlockPos p_234371_, VertexConsumer p_234372_, BlockState p_234373_, FluidState p_234374_) { boolean flag = p_234374_.is(FluidTags.LAVA); - TextureAtlasSprite[] atextureatlassprite = flag ? this.lavaIcons : this.waterIcons; @@ -20,6 +47,26 @@ float f = (float)(i >> 16 & 0xFF) / 255.0F; float f1 = (float)(i >> 8 & 0xFF) / 255.0F; float f2 = (float)(i & 0xFF) / 255.0F; +@@ -87,13 +_,13 @@ + FluidState fluidstate4 = blockstate4.getFluidState(); + BlockState blockstate5 = p_234370_.getBlockState(p_234371_.relative(Direction.EAST)); + FluidState fluidstate5 = blockstate5.getFluidState(); +- boolean flag1 = !isNeighborSameFluid(p_234374_, fluidstate1); +- boolean flag2 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.DOWN, fluidstate) ++ boolean flag1 = !isNeighborStateHidingOverlay(p_234374_, blockstate1, Direction.DOWN); ++ boolean flag2 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.DOWN, blockstate) + && !isFaceOccludedByNeighbor(p_234370_, p_234371_, Direction.DOWN, 0.8888889F, blockstate); +- boolean flag3 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.NORTH, fluidstate2); +- boolean flag4 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.SOUTH, fluidstate3); +- boolean flag5 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.WEST, fluidstate4); +- boolean flag6 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.EAST, fluidstate5); ++ boolean flag3 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.NORTH, blockstate2); ++ boolean flag4 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.SOUTH, blockstate3); ++ boolean flag5 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.WEST, blockstate4); ++ boolean flag6 = shouldRenderFace(p_234370_, p_234371_, p_234374_, p_234373_, Direction.EAST, blockstate5); + if (flag1 || flag2 || flag6 || flag5 || flag3 || flag4) { + float f3 = p_234370_.getShade(Direction.DOWN, true); + float f4 = p_234370_.getShade(Direction.UP, true); @@ -181,15 +_,15 @@ float f57 = f4 * f; float f29 = f4 * f1; diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index f193bfff1e..acbac2abd6 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -1019,4 +1019,16 @@ default BubbleColumnDirection getBubbleColumnDirection(BlockState state) { return BubbleColumnDirection.NONE; } } + + /** + * Determines if a fluid adjacent to the block on the given side should not be rendered. + * + * @param state the block state of the block + * @param selfFace the face of this block that the fluid is adjacent to + * @param adjacentFluid the fluid that is touching that face + * @return true if this block should cause the fluid's face to not render + */ + default boolean shouldHideAdjacentFluidFace(BlockState state, Direction selfFace, FluidState adjacentFluid) { + return state.getFluidState().getType().isSame(adjacentFluid.getType()); + } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java index b1f5b3a2a5..fdcd8eee09 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java @@ -755,4 +755,15 @@ default boolean isEmpty() { default BubbleColumnDirection getBubbleColumnDirection() { return self().getBlock().getBubbleColumnDirection(self()); } + + /** + * Determines if a fluid adjacent to the block on the given side should not be rendered. + * + * @param selfFace the face of this block that the fluid is adjacent to + * @param adjacentFluid the fluid that is touching that face + * @return true if this block should cause the fluid's face to not render + */ + default boolean shouldHideAdjacentFluidFace(Direction selfFace, FluidState adjacentFluid) { + return self().getBlock().shouldHideAdjacentFluidFace(self(), selfFace, adjacentFluid); + } } diff --git a/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json new file mode 100644 index 0000000000..ed35a1c6c0 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/blockstates/water_glass.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_test_water_glass_face_removal:block/water_glass" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json new file mode 100644 index 0000000000..e016ca7125 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_test_water_glass_face_removal.water_glass": "Water Glass" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json new file mode 100644 index 0000000000..10740130a9 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_test_water_glass_face_removal/models/block/water_glass.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_all", + "render_type": "minecraft:cutout", + "textures": { + "all": "minecraft:block/glass" + } +} \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java new file mode 100644 index 0000000000..6366e7c555 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/fluid/ClientFluidTests.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.fluid; + +import net.minecraft.client.renderer.block.LiquidBlockRenderer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.TransparentBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.neoforge.client.model.generators.BlockStateProvider; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.gametest.EmptyTemplate; +import net.neoforged.testframework.registration.RegistrationHelper; + +@ForEachTest(groups = ClientFluidTests.GROUP, side = Dist.CLIENT) +public class ClientFluidTests { + public static final String GROUP = "level.fluid.client"; + + static class WaterGlassBlock extends TransparentBlock { + private static final Direction HIDE_DIRECTION = Direction.NORTH; + + public WaterGlassBlock(Properties p_309186_) { + super(p_309186_); + } + + @Override + public boolean shouldHideAdjacentFluidFace(BlockState state, Direction selfFace, FluidState adjacentFluid) { + if (selfFace == HIDE_DIRECTION) { + return adjacentFluid.getFluidType() == Fluids.WATER.getFluidType(); + } else { + return super.shouldHideAdjacentFluidFace(state, selfFace, adjacentFluid); + } + } + } + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if blocks can prevent neighboring fluids from rendering against them") + static void testWaterGlassFaceRemoval(final DynamicTest test, final RegistrationHelper reg) { + final var glass = reg.blocks().registerBlock("water_glass", WaterGlassBlock::new, BlockBehaviour.Properties.ofFullCopy(Blocks.GLASS)).withLang("Water Glass").withBlockItem(); + reg.provider(BlockStateProvider.class, prov -> prov.simpleBlock(glass.get(), prov.models() + .cubeAll("water_glass", ResourceLocation.withDefaultNamespace("block/glass")) + .renderType("cutout"))); + final var waterPosition = new BlockPos(1, 1, 2); + final var glassDirection = WaterGlassBlock.HIDE_DIRECTION.getOpposite(); + final var glassPosition = waterPosition.relative(glassDirection); + test.onGameTest(helper -> helper.startSequence() + .thenExecute(() -> helper.setBlock(glassPosition, glass.get().defaultBlockState())) + .thenExecute(() -> helper.setBlock(waterPosition, Blocks.WATER.defaultBlockState())) + // Check that the north side of the water is not rendered + .thenExecute(() -> helper.assertFalse( + LiquidBlockRenderer.shouldRenderFace(helper.getLevel(), helper.absolutePos(waterPosition), + helper.getBlockState(waterPosition).getFluidState(), + helper.getBlockState(waterPosition), + glassDirection, + helper.getBlockState(glassPosition)), + "Fluid face rendering is not skipped")) + .thenSucceed()); + } +}