From 62e3cf3fd1ce3d1163f540cdcfddb9c39844e9be Mon Sep 17 00:00:00 2001 From: douira Date: Mon, 2 Dec 2024 17:01:01 +0100 Subject: [PATCH] Use larger bounding box for nearby sections in frustum check (#2879) This fixes some problems where very large block entities in nearby sections may be incorrectly culled. But it does not comprehensively fix the problem for all other sections, since that would require visiting the 27-neighborhood of every section, which is too slow. --- .../chunk/occlusion/OcclusionCuller.java | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java index e9714a21e1..e2cd453b2c 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java @@ -37,6 +37,8 @@ public void findVisible(Visitor visitor, while (queues.flip()) { processQueue(visitor, viewport, searchDistance, useOcclusionCulling, frame, queues.read(), queues.write()); } + + this.addNearbySections(visitor, viewport, searchDistance, frame); } private static void processQueue(Visitor visitor, @@ -208,13 +210,54 @@ private static int nearestToZero(int min, int max) { // The bounding box of a chunk section must be large enough to contain all possible geometry within it. Block models // can extend outside a block volume by +/- 1.0 blocks on all axis. Additionally, we make use of a small epsilon // to deal with floating point imprecision during a frustum check (see GH#2132). - private static final float CHUNK_SECTION_SIZE = 8.0f /* chunk bounds */ + 1.0f /* maximum model extent */ + 0.125f /* epsilon */; + private static final float CHUNK_SECTION_RADIUS = 8.0f /* chunk bounds */; + private static final float CHUNK_SECTION_SIZE = CHUNK_SECTION_RADIUS + 1.0f /* maximum model extent */ + 0.125f /* epsilon */; public static boolean isWithinFrustum(Viewport viewport, RenderSection section) { return viewport.isBoxVisible(section.getCenterX(), section.getCenterY(), section.getCenterZ(), CHUNK_SECTION_SIZE, CHUNK_SECTION_SIZE, CHUNK_SECTION_SIZE); } + // this bigger chunk section size is only used for frustum-testing nearby sections with large models + private static final float CHUNK_SECTION_SIZE_NEARBY = CHUNK_SECTION_RADIUS + 2.0f /* bigger model extent */ + 0.125f /* epsilon */; + + public static boolean isWithinNearbySectionFrustum(Viewport viewport, RenderSection section) { + return viewport.isBoxVisible(section.getCenterX(), section.getCenterY(), section.getCenterZ(), + CHUNK_SECTION_SIZE_NEARBY, CHUNK_SECTION_SIZE_NEARBY, CHUNK_SECTION_SIZE_NEARBY); + } + + // This method visits sections near the origin that are not in the path of the graph traversal + // but have bounding boxes that may intersect with the frustum. It does this additional check + // for all neighboring, even diagonally neighboring, sections around the origin to render them + // if their extended bounding box is visible, and they may render large models that extend + // outside the 16x16x16 base volume of the section. + private void addNearbySections(Visitor visitor, Viewport viewport, float searchDistance, int frame) { + var origin = viewport.getChunkCoord(); + var originX = origin.getX(); + var originY = origin.getY(); + var originZ = origin.getZ(); + + for (var dx = -1; dx <= 1; dx++) { + for (var dy = -1; dy <= 1; dy++) { + for (var dz = -1; dz <= 1; dz++) { + if (dx == 0 && dy == 0 && dz == 0) { + continue; + } + + var section = this.getRenderSection(originX + dx, originY + dy, originZ + dz); + + // additionally render not yet visited but visible sections + if (section != null && section.getLastVisibleFrame() != frame && isWithinNearbySectionFrustum(viewport, section)) { + // reset state on first visit, but don't enqueue + section.setLastVisibleFrame(frame); + + visitor.visit(section); + } + } + } + } + } + private void init(Visitor visitor, WriteQueue queue, Viewport viewport,