Skip to content

Commit

Permalink
Fix culling behavior between transparent and opaque blocks
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jellysquid3 committed Nov 18, 2024
1 parent a51076c commit 990cf64
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 990cf64

Please sign in to comment.