From 2c266d2495d4e70f23e59572d5baefcc635e3286 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 19 Apr 2026 21:48:26 -0500 Subject: [PATCH] Fix LODs loading outside render distance Fixes !1233 --- .../core/render/QuadTree/LodQuadTree.java | 25 +++++++- .../core/util/objects/quadTree/QuadTree.java | 63 +++++++++++++++---- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java index 1867601ea..4cf96dc4f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/QuadTree/LodQuadTree.java @@ -245,7 +245,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen //===================// //region - // remove out of bounds sections + // remove out of bound sections this.setCenterBlockPos(playerPos, (renderSection) -> { if (renderSection != null) @@ -497,7 +497,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen // create the node if (quadNode == null) - { + { rootNode.setValue(sectionPos, new LodRenderSection(sectionPos, this, this.level, this.fullDataSourceProvider)); quadNode = rootNode.getNode(sectionPos); } @@ -506,6 +506,16 @@ public class LodQuadTree extends QuadTree implements IDebugRen LodUtil.assertNotReach("Unable to add node with pos ["+DhSectionPos.toString(sectionPos)+"] to tree root ["+rootNode+"]."); } + + // Skip sections that are out-of-bounds. + // If not done some sections will appear and/or generate + // outside the desired render distance + if (!this.isSectionPosInBounds(quadNode.sectionPos)) + { + return; + } + + // make sure the render section is created (shouldn't be necessary, but just in case) LodRenderSection renderSection = quadNode.value; if (renderSection == null) @@ -1121,6 +1131,17 @@ public class LodQuadTree extends QuadTree implements IDebugRen { this.populateListWithEnabledRenderSections(this.debugNodeList); + //// can be uncommented for debugging/finding a specific position + //debugRenderer.makeParticle( + // new AbstractDebugWireframeRenderer.BoxParticle( + // new AbstractDebugWireframeRenderer.Box( + // DhSectionPos.encode((byte)7, 3,-1) + // , -64, 400, + // 0.1f, + // Color.YELLOW), + // 0.5, 0f + // )); + for (int i = 0; i < this.debugNodeList.size(); i++) { LodRenderSection renderSection = this.debugNodeList.get(i); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java index e6ec31034..67f6614cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/quadTree/QuadTree.java @@ -359,19 +359,11 @@ public class QuadTree { this.centerBlockPos = newCenterPos; - MovableGridRingList.Pos2D expectedCenterPos = new MovableGridRingList.Pos2D( - BitShiftUtil.divideByPowerOfTwo(this.centerBlockPos.x, this.treeRootDetailLevel), - BitShiftUtil.divideByPowerOfTwo(this.centerBlockPos.z, this.treeRootDetailLevel)); + int newCenterPosX = BitShiftUtil.divideByPowerOfTwo(this.centerBlockPos.x, this.treeRootDetailLevel); + int newCenterPosZ = BitShiftUtil.divideByPowerOfTwo(this.centerBlockPos.z, this.treeRootDetailLevel); - if (this.topRingList.getCenter().equals(expectedCenterPos)) - { - // tree doesn't need to be moved - return; - } - - - // remove out of bounds root nodes - this.topRingList.moveTo(expectedCenterPos.getX(), expectedCenterPos.getY(), (quadNode) -> + // remove out of bound root nodes + this.topRingList.moveTo(newCenterPosX, newCenterPosZ, (quadNode) -> { if (quadNode != null) { @@ -383,6 +375,53 @@ public class QuadTree } } }); + + // remove out of bound child nodes + this.topRingList.forEach((rootNode) -> + { + this.recursivelyClearOutOfBoundNodes(rootNode, removedItemConsumer); + }); + + } + private void recursivelyClearOutOfBoundNodes(@Nullable QuadNode quadNode, @Nullable Consumer removedItemConsumer) + { + // nodes shouldn't be null, but just in case + if (quadNode == null) + { + return; + } + + // go over each child node + for (int i = 0; i < 4; i++) + { + QuadNode childNode = quadNode.getChildByIndex(i); + if (childNode == null + || childNode.value == null) + { + // no need to go any deeper if this node is already empty + continue; + } + + // clear nodes from the bottom up + this.recursivelyClearOutOfBoundNodes(childNode, removedItemConsumer); + + // clear this node if out of bounds + if (!this.isSectionPosInBounds(childNode.sectionPos)) + { + T oldValue = childNode.value; + + // We don't remove the node until the root is removed since there isn't an + // easy way to do so. + // However, from outside the tree a null value is equivalent to the node + // not existing, so it should work fine. + childNode.value = null; + + if (removedItemConsumer != null) + { + removedItemConsumer.accept(oldValue); + } + } + } } public final DhBlockPos2D getCenterBlockPos() { return this.centerBlockPos; }