From 990cf64885b50d0180a243328a2c28d5c7c4cfb2 Mon Sep 17 00:00:00 2001 From: JellySquid Date: Sun, 17 Nov 2024 21:13:51 -0600 Subject: [PATCH] Fix culling behavior between transparent and opaque blocks Minecraft 1.21.2 changed some of the rules, and this was causing the faces of transparent blocks to be rendered even when they were hidden by full opaque blocks. Fixes #2850 --- .../compile/pipeline/BlockOcclusionCache.java | 61 +++++++++++-------- .../neoforge/block/NeoForgeBlockAccess.java | 2 +- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java index c4fbcfe8c2..5edb14622c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache.java @@ -30,53 +30,62 @@ public BlockOcclusionCache() { } /** - * @param selfState The state of the block in the level + * @param selfBlockState The state of the block in the level * @param view The block view for this render context * @param selfPos The position of the block * @param facing The facing direction of the side to check * @return True if the block side facing {@param dir} is not occluded, otherwise false */ - public boolean shouldDrawSide(BlockState selfState, BlockGetter view, BlockPos selfPos, Direction facing) { - BlockPos.MutableBlockPos otherPos = this.cachedPositionObject; - otherPos.set(selfPos.getX() + facing.getStepX(), selfPos.getY() + facing.getStepY(), selfPos.getZ() + facing.getStepZ()); + public boolean shouldDrawSide(BlockState selfBlockState, BlockGetter view, BlockPos selfPos, Direction facing) { + BlockPos.MutableBlockPos neighborPos = this.cachedPositionObject; + neighborPos.setWithOffset(selfPos, facing); - BlockState otherState = view.getBlockState(otherPos); + // The block state of the neighbor + BlockState neighborBlockState = view.getBlockState(neighborPos); - // Blocks can define special behavior to control whether faces are rendered. + // The cull shape of the neighbor between the block being rendered and it + VoxelShape neighborShape = neighborBlockState.getFaceOcclusionShape(DirectionUtil.getOpposite(facing)); + + // Minecraft enforces that if the neighbor has a full-block occlusion shape, the face is always hidden + if (isFullShape(neighborShape)) { + return false; + } + + // Blocks can define special behavior to control whether their faces are rendered. // This is mostly used by transparent blocks (Leaves, Glass, etc.) to not render interior faces between blocks // of the same type. - if (selfState.skipRendering(otherState, facing) || PlatformBlockAccess.getInstance().shouldSkipRender(view, selfState, otherState, selfPos, otherPos, facing)) { + if (selfBlockState.skipRendering(neighborBlockState, facing)) { + return false; + } else if (PlatformBlockAccess.getInstance() + .shouldSkipRender(view, selfBlockState, neighborBlockState, selfPos, neighborPos, facing)) { return false; } - // If the other block is transparent, then it is unable to hide any geometry. - if (!otherState.canOcclude()) { + // After any custom behavior has been handled, check if the neighbor block is transparent or has an empty + // cull shape. These blocks cannot hide any geometry. + if (isEmptyShape(neighborShape) || !neighborBlockState.canOcclude()) { return true; } - // The cull shape of the block being rendered - VoxelShape selfShape = selfState.getFaceOcclusionShape(facing); + // The cull shape between of the block being rendered, between it and the neighboring block + VoxelShape selfShape = selfBlockState.getFaceOcclusionShape(facing); - // If the block being rendered has an empty cull shape, intersection tests will always fail - if (selfShape.isEmpty()) { + // If the block being rendered has an empty cull shape, there will be no intersection with the neighboring + // block's cull shape, so no geometry can be hidden. + if (isEmptyShape(selfShape)) { return true; } - // The cull shape of the block neighboring the one being rendered - VoxelShape otherShape = otherState.getFaceOcclusionShape(DirectionUtil.getOpposite(facing)); - - // If the other block has an empty cull shape, then it cannot hide any geometry - if (otherShape.isEmpty()) { - return true; - } + // No other simplifications apply, so we need to perform a full shape comparison, which is very slow + return this.lookup(selfShape, neighborShape); + } - // If both blocks use a full-cube cull shape, then they will always hide the faces between each other - if (selfShape == Shapes.block() && otherShape == Shapes.block()) { - return false; - } + private static boolean isFullShape(VoxelShape selfShape) { + return selfShape == Shapes.block(); + } - // No other simplifications apply, so we need to perform a full shape comparison, which is very slow - return this.lookup(selfShape, otherShape); + private static boolean isEmptyShape(VoxelShape voxelShape) { + return voxelShape == Shapes.empty() || voxelShape.isEmpty(); } private boolean lookup(VoxelShape self, VoxelShape other) { diff --git a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java index 1ab263a164..f064e347d8 100644 --- a/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java +++ b/neoforge/src/main/java/net/caffeinemc/mods/sodium/neoforge/block/NeoForgeBlockAccess.java @@ -27,7 +27,7 @@ public int getLightEmission(BlockState state, BlockAndTintGetter level, BlockPos @Override public boolean shouldSkipRender(BlockGetter level, BlockState selfState, BlockState otherState, BlockPos selfPos, BlockPos otherPos, Direction facing) { - return (otherState.hidesNeighborFace(level, otherPos, selfState, DirectionUtil.getOpposite(facing))) && selfState.supportsExternalFaceHiding(); + return selfState.supportsExternalFaceHiding() && (otherState.hidesNeighborFace(level, otherPos, selfState, DirectionUtil.getOpposite(facing))); } @Override