From 5f84f01fb8dd03c757cdfc314ee13db9ed250d3a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 3 Apr 2023 21:33:42 -0500 Subject: [PATCH] Fix world gen queue and rendering for new QuadTree Iterators --- .../core/generation/WorldGenerationQueue.java | 90 +++++------------- .../seibel/lod/core/render/LodQuadTree.java | 92 ++++++++++--------- .../lod/core/render/RenderBufferHandler.java | 27 ++++-- 3 files changed, 92 insertions(+), 117 deletions(-) diff --git a/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java index a20e475b9..e776eebd4 100644 --- a/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/lod/core/generation/WorldGenerationQueue.java @@ -9,6 +9,7 @@ import com.seibel.lod.core.dependencyInjection.SingletonInjector; import com.seibel.lod.core.generation.tasks.*; import com.seibel.lod.core.pos.*; import com.seibel.lod.core.util.ThreadUtil; +import com.seibel.lod.core.util.objects.quadTree.QuadNode; import com.seibel.lod.core.util.objects.quadTree.QuadTree; import com.seibel.lod.core.util.objects.UncheckedInterruptedException; import com.seibel.lod.core.logging.DhLoggerBuilder; @@ -20,8 +21,6 @@ import org.apache.logging.log4j.Logger; import java.io.Closeable; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; public class WorldGenerationQueue implements Closeable @@ -123,7 +122,7 @@ public class WorldGenerationQueue implements Closeable if (this.waitingTaskQuadTree.isSectionPosInBounds(requestPos)) { CompletableFuture future = new CompletableFuture<>(); - this.waitingTaskQuadTree.set(requestPos, new WorldGenTask(pos, requiredDataDetail, tracker, future)); + this.waitingTaskQuadTree.setValue(requestPos, new WorldGenTask(pos, requiredDataDetail, tracker, future)); return future; } else @@ -241,73 +240,28 @@ public class WorldGenerationQueue implements Closeable */ private boolean startNextWorldGenTask(DhBlockPos2D targetPos) { - final AtomicReference closestTaskRef = new AtomicReference<>(null); + long closestGenDist = Long.MAX_VALUE; - // TODO improve - this.waitingTaskQuadTree.forEachRootNode((rootQuadNode) -> + WorldGenTask closestTask = null; + + // TODO improve, having to go over every item isn't super efficient + Iterator> leafNodeIterator = this.waitingTaskQuadTree.leafNodeIterator(); + while (leafNodeIterator.hasNext()) { - if (closestTaskRef.get() == null) + WorldGenTask newGenTask = leafNodeIterator.next().value; + if (newGenTask != null) // TODO add an option to skip leaves with null values and potentially auto-prune them { - rootQuadNode.forAllLeafValues((worldGenTask) -> + + // use chebyShev distance in order to generate in rings around the target pos (also because it is a fast distance calculation) + int chebDistToTargetPos = newGenTask.pos.getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D()); + if (chebDistToTargetPos < closestGenDist) { - if (closestTaskRef.get() == null) - { - closestTaskRef.set(worldGenTask); - } - }); + // this task is closer than the last one + closestTask = newGenTask; + closestGenDist = chebDistToTargetPos; + } } - }); - - WorldGenTask closestTask = closestTaskRef.get(); - - -// // look through the tree from lowest to highest detail level to find the next task to generate -// for (byte detailLevel = QuadTree.TREE_LOWEST_DETAIL_LEVEL; detailLevel < this.waitingTaskQuadTree.treeMaxDetailLevel; detailLevel++) -// { -// // look for the task that is closest to the targetPos -// long closestGenDist = Long.MAX_VALUE; -// -// MovableGridRingList gridRingList = this.waitingTaskQuadTree.getRingList(detailLevel); -// for (WorldGenTask newGenTask : gridRingList) -// { -// if (newGenTask != null) -// { -// if (queueFirstGenerationRequestFound) -// { -// // queue the first task we can find -// closestTask = newGenTask; -// break; -// } -// else -// { -// // use chebyShev distance in order to generate in rings around the target pos (also because it is a fast distance calculation) -// int chebDistToTargetPos = newGenTask.pos.getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D()); -// if (chebDistToTargetPos < closestGenDist) -// { -// // this task is closer than the last one -// closestTask = newGenTask; -// closestGenDist = chebDistToTargetPos; -// } -// else if (closestTask != null) -// { -// // this task is farther than the last one, -// // assume we have gotten as close as we can -// // and queue the task -// break; -// } -// } -// } -// } -// -// // a task has been found, don't look at the next detail level, -// // everything there will be farther away -// if (closestTask != null) -// { -// break; -// } -// } - - + } if (closestTask == null) { @@ -318,7 +272,7 @@ public class WorldGenerationQueue implements Closeable // remove the task we found, we are going to start it and don't want to run it multiple times - WorldGenTask removedWorldGenTask = this.waitingTaskQuadTree.set(new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z), null); + WorldGenTask removedWorldGenTask = this.waitingTaskQuadTree.setValue(new DhSectionPos(closestTask.pos.detailLevel, closestTask.pos.x, closestTask.pos.z), null); // removedWorldGenTask can be null // TODO when? @@ -366,9 +320,9 @@ public class WorldGenerationQueue implements Closeable childFutures.add(newFuture); WorldGenTask newGenTask = new WorldGenTask(new DhLodPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), childDhSectionPos.sectionDetailLevel, removedWorldGenTask.taskTracker, newFuture); - this.waitingTaskQuadTree.set(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask); + this.waitingTaskQuadTree.setValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ), newGenTask); - boolean valueAdded = this.waitingTaskQuadTree.get(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ)) != null; + boolean valueAdded = this.waitingTaskQuadTree.getValue(new DhSectionPos(childDhSectionPos.sectionDetailLevel, childDhSectionPos.sectionX, childDhSectionPos.sectionZ)) != null; LodUtil.assertTrue(valueAdded); // failed to add world gen task to quad tree, this means the quad tree was the wrong size // LOGGER.info("split feature "+sectionPos+" into "+childDhSectionPos+" "+(valueAdded ? "added" : "notAdded")); diff --git a/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java index fc1536105..d61550c72 100644 --- a/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/lod/core/render/LodQuadTree.java @@ -11,6 +11,8 @@ import com.seibel.lod.core.util.objects.quadTree.QuadNode; import com.seibel.lod.core.util.objects.quadTree.QuadTree; import org.apache.logging.log4j.Logger; +import java.util.Iterator; + /** * This quadTree structure is our core data structure and holds * all rendering data.

@@ -82,8 +84,8 @@ public class LodQuadTree extends QuadTree implements AutoClose * @param z z coordinate of the section * @return the LodSection */ - public LodRenderSection getSection(byte detailLevel, int x, int z) { return this.get(new DhSectionPos(detailLevel, x, z)); } - public LodRenderSection getSection(DhSectionPos pos) { return this.get(pos); } + public LodRenderSection getSection(byte detailLevel, int x, int z) { return this.getValue(new DhSectionPos(detailLevel, x, z)); } + public LodRenderSection getSection(DhSectionPos pos) { return this.getValue(pos); } @@ -163,21 +165,34 @@ public class LodQuadTree extends QuadTree implements AutoClose } private void updateAllRenderSections(DhBlockPos2D playerPos) { - this.forEachRootNodePos((rootNode, rootSectionPos) -> + // make sure all root nodes are created + Iterator rootPosIterator = this.rootNodePosIterator(); + while (rootPosIterator.hasNext()) { - if (rootNode == null) + DhSectionPos rootSectionPos = rootPosIterator.next(); + if (this.getNode(rootSectionPos) == null) { LodRenderSection newRenderSection = new LodRenderSection(rootSectionPos); - this.set(rootSectionPos, newRenderSection); - return; // update next tick + this.setValue(rootSectionPos, newRenderSection); } + } + + + // update all nodes in the tree + Iterator rootNodeIterator = this.rootNodePosIterator(); + while (rootNodeIterator.hasNext()) + { + DhSectionPos rootPos = rootNodeIterator.next(); + QuadNode rootNode = this.getNode(rootPos); // should never be null - - rootNode.forEachDirectChildNode((quadNode, sectionPos) -> + // iterate over nodes in this root + Iterator> nodeIterator = rootNode.getNodeIterator(); + while (nodeIterator.hasNext()) { - recursivelyUpdateRenderSectionNode(playerPos, rootNode, quadNode, sectionPos); - }); - }); + QuadNode quadNode = nodeIterator.next(); + recursivelyUpdateRenderSectionNode(playerPos, rootNode, quadNode, quadNode.sectionPos); + } + } } private void recursivelyUpdateRenderSectionNode(DhBlockPos2D playerPos, QuadNode rootNode, QuadNode nullableQuadNode, DhSectionPos sectionPos) { @@ -215,10 +230,15 @@ public class LodQuadTree extends QuadTree implements AutoClose } else { - nullableQuadNode.forEachDirectChildNode((childQuadNode, childSectionPosition) -> + // TODO this never returns anything + Iterator childPosIterator = nullableQuadNode.getChildPosIterator(); + while (childPosIterator.hasNext()) { - recursivelyUpdateRenderSectionNode(playerPos, rootNode, childQuadNode, childSectionPosition); - }); + DhSectionPos childPos = childPosIterator.next(); + QuadNode childNode = rootNode.getNode(childPos); + + recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos); + } } } // TODO this should only equal the expected detail level, the (expectedDetailLevel-1) is a temporary fix to prevent corners from being cut out @@ -336,28 +356,16 @@ public class LodQuadTree extends QuadTree implements AutoClose { LOGGER.info("Clearing render cache..."); - this.forEachRootNode((rootNode) -> + Iterator> nodeIterator = this.nodeIterator(); + while (nodeIterator.hasNext()) { - rootNode.forEachDirectChildNode((quadNode, sectionPos) -> + QuadNode quadNode = nodeIterator.next(); + if (quadNode.value != null) { - if (quadNode != null && quadNode.value != null) - { - quadNode.value.disposeRenderData(); - } - }); - }); - - this.forEachRootNode((rootNode) -> - { - rootNode.forEachDirectChildNode((quadNode, sectionPos) -> - { - if (quadNode != null && quadNode.value != null) - { - quadNode.value.disposeRenderData(); - quadNode.value = null; - } - }); - }); + quadNode.value.disposeRenderData(); + quadNode.value = null; + } + } // delete the cache files this.renderSourceProvider.deleteRenderCache(); @@ -402,16 +410,16 @@ public class LodQuadTree extends QuadTree implements AutoClose { LOGGER.info("Shutting down "+ LodQuadTree.class.getSimpleName()+"..."); - this.forEachRootNode((rootNode) -> + Iterator> nodeIterator = this.nodeIterator(); + while (nodeIterator.hasNext()) { - rootNode.forEachDirectChildNode((quadNode, sectionPos) -> + QuadNode quadNode = nodeIterator.next(); + if (quadNode.value != null) { - if (quadNode != null && quadNode.value != null) - { - quadNode.value.disposeRenderData(); - } - }); - }); + quadNode.value.disposeRenderData(); + quadNode.value = null; + } + } LOGGER.info("Finished shutting down "+ LodQuadTree.class.getSimpleName()); } diff --git a/core/src/main/java/com/seibel/lod/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/lod/core/render/RenderBufferHandler.java index 072d2cd9f..1ab58d137 100644 --- a/core/src/main/java/com/seibel/lod/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/lod/core/render/RenderBufferHandler.java @@ -9,9 +9,11 @@ import com.seibel.lod.core.render.renderer.LodRenderer; import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.math.Vec3f; import com.seibel.lod.core.util.objects.SortedArraySet; +import com.seibel.lod.core.util.objects.quadTree.QuadNode; import org.apache.logging.log4j.Logger; import java.util.Comparator; +import java.util.Iterator; /** * This object tells the {@link LodRenderer} what buffers to render @@ -135,8 +137,15 @@ public class RenderBufferHandler // Build the sorted list this.loadedNearToFarBuffers = new SortedArraySet<>((a, b) -> -farToNearComparator.compare(a, b)); // TODO is the comparator named wrong? - this.quadTree.forEachValue((renderSection, sectionPos) -> + + Iterator> nodeIterator = this.quadTree.nodeIterator(); + while (nodeIterator.hasNext()) { + QuadNode node = nodeIterator.next(); + + DhSectionPos sectionPos = node.sectionPos; + LodRenderSection renderSection = node.value; + if (renderSection != null && renderSection.shouldRender()) { if (renderSection.renderBufferRef.get() != null && renderSection.renderBufferRef.get().areBuffersUploaded()) @@ -144,7 +153,7 @@ public class RenderBufferHandler this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(renderSection.renderBufferRef.get(), sectionPos)); } } - }); + } } public void renderOpaque(LodRenderer renderContext) @@ -160,8 +169,10 @@ public class RenderBufferHandler public void update() { - this.quadTree.forEachValue((renderSection, sectionPos) -> + Iterator> nodeIterator = this.quadTree.nodeIterator(); + while (nodeIterator.hasNext()) { + LodRenderSection renderSection = nodeIterator.next().value; if (renderSection != null) { ColumnRenderSource currentRenderSource = renderSection.getRenderSource(); @@ -182,19 +193,21 @@ public class RenderBufferHandler currentRenderSource.trySwapInNewlyBuiltRenderBuffer(renderSection.getRenderSource(), renderSection.renderBufferRef); } } - }); + } } public void close() - { - this.quadTree.forEachValue((renderSection) -> + { + Iterator> nodeIterator = this.quadTree.nodeIterator(); + while (nodeIterator.hasNext()) { + LodRenderSection renderSection = nodeIterator.next().value; if (renderSection != null && renderSection.renderBufferRef.get() != null) { renderSection.renderBufferRef.get().close(); renderSection.renderBufferRef.set(null); } - }); + } }