diff --git a/api/src/main/java/com/seibel/lod/core/util/BitShiftUtil.java b/api/src/main/java/com/seibel/lod/core/util/BitShiftUtil.java
index 38011672a..c97f737f8 100644
--- a/api/src/main/java/com/seibel/lod/core/util/BitShiftUtil.java
+++ b/api/src/main/java/com/seibel/lod/core/util/BitShiftUtil.java
@@ -32,6 +32,15 @@ public class BitShiftUtil
*/
public static int half(int value) { return value >> 1; }
+ /**
+ * Equivalent to:
+ * value >> power,
+ * value / 2^power
+ *
+ * Note: value / 2^power isn't identical for negative values
+ */
+ public static int divideByPowerOfTwo(int value, int power) { return value >> power; }
+
/**
* Equivalent to:
* value << 1,
diff --git a/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java
index 8edc3e715..eeb1af617 100644
--- a/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java
+++ b/core/src/main/java/com/seibel/lod/core/level/DhClientServerLevel.java
@@ -96,7 +96,7 @@ public class DhClientServerLevel implements IDhClientLevel, IDhServerLevel
if (rs == null)
return;
- if (rs.tree.viewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH)
+ if (rs.tree.blockViewDistance != Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH)
{
if (!this.renderState.compareAndSet(rs, null))
return; //If we fail, we'll just wait for the next tick
diff --git a/core/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java
index d755ecfbf..8ea3c2ad2 100644
--- a/core/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java
+++ b/core/src/main/java/com/seibel/lod/core/logging/f3/F3Screen.java
@@ -9,90 +9,129 @@ import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;
-public class F3Screen {
- public static boolean renderCustomF3 = true;
-
- private static final String[] DEFAULT_STR = {
- "",
- ModInfo.READABLE_NAME + " version: " + ModInfo.VERSION
- };
- private static final LinkedList> selfUpdateMessages = new LinkedList<>();
- public static void addStringToDisplay(List list) {
- list.addAll(Arrays.asList(DEFAULT_STR));
- Iterator> it = selfUpdateMessages.iterator();
- while (it.hasNext()) {
- WeakReference ref = it.next();
- Message msg = ref.get();
- if (msg == null) {
- it.remove();
- } else {
- msg.printTo(list);
- }
- }
- }
-
- @SuppressWarnings("unused")
- public static abstract class Message {
- protected Message() {
- selfUpdateMessages.add(new WeakReference<>(this));
- }
-
- public abstract void printTo(List output);
- }
-
- @SuppressWarnings("unused")
- public static class StaticMessage extends Message {
- private final String[] lines;
- public StaticMessage(String... lines) {
- this.lines = lines;
- }
- @Override
- public void printTo(List output) {
- output.addAll(Arrays.asList(lines));
- }
- }
-
- @SuppressWarnings("unused")
- public static class DynamicMessage extends Message {
- private final Supplier supplier;
- public DynamicMessage(Supplier message) {
- this.supplier = message;
- }
- public void printTo(List list) {
- String msg = supplier.get();
- if (msg != null) {
- list.add(msg);
- }
- }
- }
- @SuppressWarnings("unused")
- public static class MultiDynamicMessage extends Message {
- private final Supplier[] supplier;
- @SafeVarargs
- public MultiDynamicMessage(Supplier... messages) {
- this.supplier = messages;
- }
- public void printTo(List list) {
- for (Supplier s : supplier) {
- String msg = s.get();
- if (msg != null) {
- list.add(msg);
- }
- }
- }
- }
-
- public static class NestedMessage extends Message {
- private final Supplier supplier;
- public NestedMessage(Supplier message) {
- this.supplier = message;
- }
- public void printTo(List list) {
- String[] msg = supplier.get();
- if (msg != null) {
- list.addAll(Arrays.asList(msg));
- }
- }
-
- }
+public class F3Screen
+{
+ public static boolean renderCustomF3 = true;
+
+ private static final String[] DEFAULT_STR = {
+ "",
+ ModInfo.READABLE_NAME + " version: " + ModInfo.VERSION
+ };
+ private static final LinkedList> selfUpdateMessages = new LinkedList<>();
+
+ public static void addStringToDisplay(List list)
+ {
+ list.addAll(Arrays.asList(DEFAULT_STR));
+ Iterator> iterator = selfUpdateMessages.iterator();
+ while (iterator.hasNext())
+ {
+ WeakReference ref = iterator.next();
+ Message message = ref.get();
+ if (message == null)
+ {
+ iterator.remove();
+ }
+ else
+ {
+ message.printTo(list);
+ }
+ }
+ }
+
+
+
+ //================//
+ // helper classes //
+ //================//
+
+ public static abstract class Message
+ {
+ protected Message()
+ {
+ selfUpdateMessages.add(new WeakReference<>(this));
+ }
+
+ public abstract void printTo(List output);
+ }
+
+ @SuppressWarnings("unused")
+ public static class StaticMessage extends Message
+ {
+ private final String[] lines;
+
+ public StaticMessage(String... lines)
+ {
+ this.lines = lines;
+ }
+
+ @Override
+ public void printTo(List output)
+ {
+ output.addAll(Arrays.asList(lines));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public static class DynamicMessage extends Message
+ {
+ private final Supplier supplier;
+
+ public DynamicMessage(Supplier message)
+ {
+ this.supplier = message;
+ }
+
+ public void printTo(List list)
+ {
+ String msg = supplier.get();
+ if (msg != null)
+ {
+ list.add(msg);
+ }
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public static class MultiDynamicMessage extends Message
+ {
+ private final Supplier[] supplier;
+
+ @SafeVarargs
+ public MultiDynamicMessage(Supplier... messages)
+ {
+ this.supplier = messages;
+ }
+
+ public void printTo(List list)
+ {
+ for (Supplier s : supplier)
+ {
+ String msg = s.get();
+ if (msg != null)
+ {
+ list.add(msg);
+ }
+ }
+ }
+ }
+
+ public static class NestedMessage extends Message
+ {
+ private final Supplier supplier;
+
+ public NestedMessage(Supplier message)
+ {
+ this.supplier = message;
+ }
+
+ public void printTo(List list)
+ {
+ String[] msg = supplier.get();
+ if (msg != null)
+ {
+ list.addAll(Arrays.asList(msg));
+ }
+ }
+ }
+
}
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 9db82d7f2..e5393b826 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
@@ -7,125 +7,132 @@ import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.file.renderfile.IRenderSourceProvider;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.pos.Pos2D;
+import com.seibel.lod.core.util.BitShiftUtil;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.MathUtil;
import com.seibel.lod.core.util.gridList.MovableGridRingList;
import org.apache.logging.log4j.Logger;
-// QuadTree built from several layers of 2d ring buffers
-
/**
- * This quadTree structure is the core of the DH mod.
- * This class represent a circular quadTree of lodSection
- *
- * Each section at level n is populated in one (sometimes more than one) ways:
- * -by constructing it from the data of all the children sections (lower levels)
- * -by loading from file
- * -by adding data with the lodBuilder
+ * This quadTree structure is the core of the DH mod.
+ *
+ * This class represent a circular quadTree of lodSections.
+ * Each section at level n is populated in one or more ways:
+ * -by constructing it from the data of all the children sections (lower levels)
+ * -by loading from file
+ * -by adding data with the lodBuilder
+ *
+ * The QuadTree is built from several layers of 2d ring buffers.
*/
-public class LodQuadTree implements AutoCloseable {
-
+public class LodQuadTree implements AutoCloseable
+{
/**
- * Note: all config value should be via the class that extends this class, and
+ * Note: all config values should be via the class that extends this class, and
* by implementing different abstract methods
*/
- private static final byte LAYER_BEGINNING_OFFSET = ColumnRenderSource.SECTION_SIZE_OFFSET;
+ private static final byte TREE_LOWEST_DETAIL_LEVEL = ColumnRenderSource.SECTION_SIZE_OFFSET;
private static final boolean SUPER_VERBOSE_LOGGING = false;
- public final byte getLayerDataDetailOffset(byte sectionDetail) {
- return ColumnRenderSource.SECTION_SIZE_OFFSET;
- }
- public final byte getLayerSectionDetailOffset(byte dataDetail) {
- return ColumnRenderSource.SECTION_SIZE_OFFSET;
- }
- public final byte getLayerDataDetail(byte sectionDetail) {
- return (byte) (sectionDetail - getLayerDataDetailOffset(sectionDetail));
- }
- public final byte getLayerSectionDetail(byte dataDetail) {
- return (byte) (dataDetail + getLayerSectionDetailOffset(dataDetail));
- }
-
- private static final Logger LOGGER = DhLoggerBuilder.getLogger("LodQuadTree");
-
- public final byte numbersOfSectionLevels;
- private final MovableGridRingList[] ringLists;
- /** measured in blocks */
- public final int viewDistance;
+
+
+ public final byte getLayerDataDetailOffset() { return ColumnRenderSource.SECTION_SIZE_OFFSET; }
+ public final byte getLayerDataDetail(byte sectionDetailLevel) { return (byte) (sectionDetailLevel - this.getLayerDataDetailOffset()); }
+
+ public final byte getLayerSectionDetailOffset() { return ColumnRenderSource.SECTION_SIZE_OFFSET; }
+ public final byte getLayerSectionDetail(byte dataDetail) { return (byte) (dataDetail + this.getLayerSectionDetailOffset()); }
+
+
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+
+ /** AKA number of section layers? - TODO James */
+ public final byte numbersOfSectionDetailLevels;
+ private final MovableGridRingList[] renderSectionRingLists;
+ public final int blockViewDistance;
private final IRenderSourceProvider renderSourceProvider;
-
+
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
-
+
+
+
/**
* Constructor of the quadTree
* @param viewDistance View distance in blocks
- * @param initialPlayerX player x coordinate
- * @param initialPlayerZ player z coordinate
+ * @param initialPlayerX player x block coordinate
+ * @param initialPlayerZ player z block coordinate
*/
- public LodQuadTree(IDhClientLevel level, int viewDistance, int initialPlayerX, int initialPlayerZ, IRenderSourceProvider provider) {
+ public LodQuadTree(
+ IDhClientLevel level, int viewDistance,
+ int initialPlayerX, int initialPlayerZ,
+ IRenderSourceProvider provider)
+ {
DetailDistanceUtil.updateSettings(); //TODO: Move this to somewhere else
this.level = level;
- renderSourceProvider = provider;
- this.viewDistance = viewDistance;
-
- { // Calculate the max section detail
- byte maxDataDetailLevel = getMaxDetailInRange(viewDistance * Math.sqrt(2));
- byte topSectionLevel = getLayerSectionDetail(maxDataDetailLevel);
- numbersOfSectionLevels = (byte) (topSectionLevel + 1);
- ringLists = new MovableGridRingList[numbersOfSectionLevels - LAYER_BEGINNING_OFFSET];
- }
-
- { // Construct the ringLists
- LOGGER.info("Creating ringLists with player center at {}", new Pos2D(initialPlayerX, initialPlayerZ));
- for (byte i = LAYER_BEGINNING_OFFSET; i < numbersOfSectionLevels; i++) {
- byte targetDataDetail = getLayerDataDetail(i);
- int maxDist = getFurthestDistance(targetDataDetail);
- int halfSize = MathUtil.ceilDiv(maxDist, (1 << i)) + 8; // +8 to make sure the section is fully contained in the ringList
- {
- DhSectionPos checkerPos = new DhSectionPos(i, halfSize, halfSize);
- byte checkedDetail = calculateExpectedDetailLevel(new DhBlockPos2D(initialPlayerX, initialPlayerZ),checkerPos);
- LodUtil.assertTrue(checkedDetail > targetDataDetail,
- "in {}, getFuthestDistance return {} which would be contained in range {}, but calculateExpectedDetailLevel at {} is {} <= {}",
- i, maxDist, halfSize - 2, checkerPos, checkedDetail, targetDataDetail);
- }
- LOGGER.info("ringlist centered in {} with halfSize {} (maxDist {}, dataDetail {})", new Pos2D(initialPlayerX >> i, initialPlayerZ >> i), halfSize, maxDist, targetDataDetail);
- ringLists[i - LAYER_BEGINNING_OFFSET] = new MovableGridRingList<>(halfSize,
- initialPlayerX >> i, initialPlayerZ >> i);
- LOGGER.info("Creating ringList {}: {}", i, ringLists[i - LAYER_BEGINNING_OFFSET].toString());
- }
- }
+ this.renderSourceProvider = provider;
+ this.blockViewDistance = viewDistance;
+
+
+
+ // Calculate the max section detail level //
+
+ byte maxDetailLevel = this.getMaxDetailInRange(viewDistance * Math.sqrt(2));
+ byte topSectionDetailLevel = this.getLayerSectionDetail(maxDetailLevel);
+ this.numbersOfSectionDetailLevels = (byte) (topSectionDetailLevel + 1);
+ this.renderSectionRingLists = new MovableGridRingList[this.numbersOfSectionDetailLevels - TREE_LOWEST_DETAIL_LEVEL];
+
+
+
+ // Construct the ringLists //
+
+ LOGGER.info("Creating "+MovableGridRingList.class.getSimpleName()+" with player center at {}", new Pos2D(initialPlayerX, initialPlayerZ));
+ for (byte sectionDetailLevel = TREE_LOWEST_DETAIL_LEVEL; sectionDetailLevel < this.numbersOfSectionDetailLevels; sectionDetailLevel++)
+ {
+ byte targetDetailLevel = this.getLayerDataDetail(sectionDetailLevel);
+ int maxDist = this.getFurthestDistance(targetDetailLevel);
+ int halfSize = MathUtil.ceilDiv(maxDist, BitShiftUtil.powerOfTwo(sectionDetailLevel)) + 8; // +8 to make sure the section is fully contained in the ringList //TODO what does the "8" represent?
+
+
+ // check that the detail level and position are valid
+ DhSectionPos checkedPos = new DhSectionPos(sectionDetailLevel, halfSize, halfSize);
+ byte checkedDetailLevel = this.calculateExpectedDetailLevel(new DhBlockPos2D(initialPlayerX, initialPlayerZ), checkedPos);
+ // validate the detail level
+ LodUtil.assertTrue(checkedDetailLevel > targetDetailLevel,
+ "in "+sectionDetailLevel+", getFurthestDistance would return "+maxDist+" which would be contained in range "+(halfSize-2)+", but calculateExpectedDetailLevel at "+checkedPos+" is "+checkedDetailLevel+" <= "+targetDetailLevel);
+
+
+ // create the new ring list
+ Pos2D ringListCenterPos = new Pos2D(BitShiftUtil.divideByPowerOfTwo(initialPlayerX, sectionDetailLevel), BitShiftUtil.divideByPowerOfTwo(initialPlayerZ, sectionDetailLevel));
+
+ LOGGER.info("Creating "+MovableGridRingList.class.getSimpleName()+" centered on "+ringListCenterPos+" with halfSize ["+halfSize+"] (maxDist ["+maxDist+"], dataDetail ["+targetDetailLevel+"])");
+ this.renderSectionRingLists[sectionDetailLevel - TREE_LOWEST_DETAIL_LEVEL] = new MovableGridRingList<>(halfSize, ringListCenterPos.x, ringListCenterPos.y);
+
+ }
+
}
-
-
+
+
+
/**
* This method return the LodSection given the Section Pos
- * @param pos the section positon.
+ * @param pos the section position.
* @return the LodSection
*/
- public LodRenderSection getSection(DhSectionPos pos) {
- return getSection(pos.sectionDetailLevel, pos.sectionX, pos.sectionZ);
- }
-
+ public LodRenderSection getSection(DhSectionPos pos) { return this.getSection(pos.sectionDetailLevel, pos.sectionX, pos.sectionZ); }
+
/**
* This method returns the RingList of a given detail level
* @apiNote The returned ringList should not be modified!
* @param detailLevel the detail level
* @return the RingList
*/
- public MovableGridRingList getRingList(byte detailLevel) {
- return ringLists[detailLevel - LAYER_BEGINNING_OFFSET];
- }
-
+ public MovableGridRingList getRingList(byte detailLevel) { return this.renderSectionRingLists[detailLevel - TREE_LOWEST_DETAIL_LEVEL]; }
+
/**
* This method returns the number of detail levels in the quadTree
* @return the number of detail levels
*/
- public byte getNumbersOfSectionLevels() {
- return numbersOfSectionLevels;
- }
+ public byte getNumbersOfSectionDetailLevels() { return this.numbersOfSectionDetailLevels; }
- public byte getStartingSectionLevel() {
- return LAYER_BEGINNING_OFFSET;
- }
+ public byte getStartingSectionLevel() { return TREE_LOWEST_DETAIL_LEVEL; }
/**
* This method return the LodSection at the given detail level and level coordinate x and z
@@ -134,10 +141,11 @@ public class LodQuadTree implements AutoCloseable {
* @param z z coordinate of the section
* @return the LodSection
*/
- public LodRenderSection getSection(byte detailLevel, int x, int z) {
- return ringLists[detailLevel - LAYER_BEGINNING_OFFSET].get(x, z);
- }
+ public LodRenderSection getSection(byte detailLevel, int x, int z) { return this.renderSectionRingLists[detailLevel - TREE_LOWEST_DETAIL_LEVEL].get(x, z); }
+
+
+
/**
* This method will compute the detail level based on player position and section pos
@@ -146,7 +154,8 @@ public class LodQuadTree implements AutoCloseable {
* @param sectionPos section position
* @return detail level of this section pos
*/
- public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) {
+ public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos)
+ {
return DetailDistanceUtil.getDetailLevelFromDistance(
playerPos.dist(sectionPos.getCenter().getCenterBlockPos()));
}
@@ -159,9 +168,7 @@ public class LodQuadTree implements AutoCloseable {
* @param distance the circle radius
* @return the highest detail level in the circle
*/
- public byte getMaxDetailInRange(double distance) {
- return DetailDistanceUtil.getDetailLevelFromDistance(distance);
- }
+ public byte getMaxDetailInRange(double distance) { return DetailDistanceUtil.getDetailLevelFromDistance(distance); }
/**
* The method will return the furthest distance to the center for the given detail level
@@ -171,88 +178,72 @@ public class LodQuadTree implements AutoCloseable {
* @param detailLevel detail level
* @return the furthest distance to the center, in blocks
*/
- public int getFurthestDistance(byte detailLevel) {
+ public int getFurthestDistance(byte detailLevel)
+ {
return (int)Math.ceil(DetailDistanceUtil.getDrawDistanceFromDetail(detailLevel + 1));
// +1 because that's the border to the next detail level, and we want to include up to it.
}
/**
* Given a section pos at level n this method returns the parent section at level n+1
- * @param pos the section positon
+ * @param pos the section position
* @return the parent LodSection
*/
- public LodRenderSection getParentSection(DhSectionPos pos) {
- return getSection(pos.getParentPos());
- }
+ public LodRenderSection getParentSection(DhSectionPos pos) { return this.getSection(pos.getParentPos()); }
/**
* Given a section pos at level n and a child index this method return the
* child section at level n-1
- * @param pos
* @param child0to3 since there are 4 possible children this index identify which one we are getting
* @return one of the child LodSection
*/
- public LodRenderSection getChildSection(DhSectionPos pos, int child0to3) {
- return getSection(pos.getChildByIndex(child0to3));
- }
-
- private LodRenderSection _set(MovableGridRingList list, int x, int z, LodRenderSection t) {
- LodUtil.assertTrue(t != null, "setting null at [{},{}] in {}", x, z, list.toString());
- LodUtil.assertTrue(t.pos.sectionX == x && t.pos.sectionZ == z, "pos {} != [{},{}] in {}", t.pos, x, z, list.toString());
- LodRenderSection s = list.setChained(x,z,t);
- LodUtil.assertTrue(s != null, "returned null at [{},{}]: {}", x, z, list.toString());
- LodUtil.assertTrue(s == t,"{} != {} in {}",s,t, list.toString());
- return s;
- }
- private LodRenderSection _getNotNull(MovableGridRingList list, int x, int z) {
- LodUtil.assertTrue(list.inRange(x,z), "[{},{}] not in range of {}", x, z, list.toString());
- LodRenderSection s = list.get(x,z);
- LodUtil.assertTrue(s != null, "getting null at [{},{}] in {}", x, z, list.toString());
- LodUtil.assertTrue(s.pos.sectionX == x && s.pos.sectionZ == z, "obj {} != [{},{}] in {}", s, x, z, list.toString());
- return s;
- }
- private LodRenderSection _get(MovableGridRingList list, int x, int z) {
- LodRenderSection s = list.get(x,z);
- LodUtil.assertTrue(s == null || (s.pos.sectionX == x && s.pos.sectionZ == z), "obj {} != [{},{}] in {}", s, x, z, list.toString());
- return s;
- }
-
+ public LodRenderSection getChildSection(DhSectionPos pos, int child0to3) { return this.getSection(pos.getChildByIndex(child0to3)); }
+
+
+
+
+ // tick //
+
/**
- * This function update the quadTree based on the playerPos and the current game configs (static and global)
+ * This function updates the quadTree based on the playerPos and the current game configs (static and global)
* @param playerPos the reference position for the player
*/
- public void tick(DhBlockPos2D playerPos) {
- for (int sectLevel = LAYER_BEGINNING_OFFSET; sectLevel < numbersOfSectionLevels; sectLevel++) {
- if (!ringLists[sectLevel - LAYER_BEGINNING_OFFSET].getCenter().equals(
- new Pos2D(playerPos.x >> sectLevel, playerPos.z >> sectLevel))) {
- LOGGER.info("TreeTick: Moving ring list {} from {} to {}", sectLevel,
- ringLists[sectLevel - LAYER_BEGINNING_OFFSET].getCenter(),
- new Pos2D(playerPos.x >> sectLevel, playerPos.z >> sectLevel));
- ringLists[sectLevel - LAYER_BEGINNING_OFFSET]
- .move(playerPos.x >> sectLevel, playerPos.z >> sectLevel,
- LodRenderSection::dispose);
- }
- }
-
- // First tick pass: update all sections' childCount from bottom level to top level. Step:
- // If sectLevel is bottom && section != null:
- // - set childCount to 0
- // If section != null && child != 0: //TODO: Should I move this createChild steps to Second tick pass?
- // - // Section will be in the unloaded state.
- // - create parent if not at final level and if it doesn't exist, with childCount = 1
- // - for each child:
- // - if null, create new with childCount = 0 (force load due to neighboring issues)
- // - else if childCount == -1, set childCount = 0 (rescue it)
- // - set childCount to 4
- // Else:
- // - Calculate targetLevel at that section
- // - If sectLevel == numberOfSectionLevels - 1:
- // - // Section is the top level.
- // - If targetLevel > dataLevel@sectLevel && section != null:
- // - set childCount to -1 (Signal that section is to be freed) (this prob not be rescued as it is the top level)
- // - If targetLevel <= dataLevel@sectLevel && section == null: (direct use the current sectLevel's dataLevel)
- // - create new section with childCount = 0
- // - Else:
+ public void tick(DhBlockPos2D playerPos)
+ {
+ // recenter the grid lists if necessary
+ for (int sectionDetailLevel = TREE_LOWEST_DETAIL_LEVEL; sectionDetailLevel < this.numbersOfSectionDetailLevels; sectionDetailLevel++)
+ {
+ Pos2D expectedCenterPos = new Pos2D(BitShiftUtil.divideByPowerOfTwo(playerPos.x, sectionDetailLevel), BitShiftUtil.divideByPowerOfTwo(playerPos.z, sectionDetailLevel));
+ MovableGridRingList gridList = this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL];
+
+ if (!gridList.getCenter().equals(expectedCenterPos))
+ {
+ LOGGER.info("TreeTick: Moving ring list "+sectionDetailLevel+" from "+gridList.getCenter()+" to "+expectedCenterPos);
+ gridList.moveTo(expectedCenterPos.x, expectedCenterPos.y, LodRenderSection::dispose);
+ }
+ }
+
+
+ // TODO: inline comments should be added everywhere for this tick pass, so this comment block should be removed (having duplicate comments in two places is a bad idea)
+ // First tick pass: update all sections' childCount from bottom level to top level. Step:
+ // If sectLevel is bottom && section != null:
+ // - set childCount to 0
+ // If section != null && child != 0: //TODO: Should I move this createChild steps to Second tick pass?
+ // - // Section will be in the unloaded state.
+ // - create parent if not at final level and if it doesn't exist, with childCount = 1
+ // - for each child:
+ // - if null, create new with childCount = 0 (force load due to neighboring issues)
+ // - else if childCount == -1, set childCount = 0 (rescue it)
+ // - set childCount to 4
+ // Else:
+ // - Calculate targetLevel at that section
+ // - If sectLevel == numberOfSectionLevels - 1:
+ // - // Section is the top level.
+ // - If targetLevel > dataLevel@sectLevel && section != null:
+ // - set childCount to -1 (Signal that section is to be freed) (this prob not be rescued as it is the top level)
+ // - If targetLevel <= dataLevel@sectLevel && section == null: (direct use the current sectLevel's dataLevel)
+ // - create new section with childCount = 0
+ // - Else:
// - // Section is not the top level. So we also need to consider the parent.
// - If targetLevel >= dataLevel@(sectLevel+1) && section != null: (use the next level's dataLevel)
// - Parent's childCount-- (Assert parent != null && childCount > 0 before decrementing)
@@ -263,127 +254,268 @@ public class LodQuadTree implements AutoCloseable {
// - If targetLevel < dataLevel@(sectLevel+1) && section == null: (use the next level's dataLevel)
// - create new section with childCount = 0
// - Parent's childCount++ (Create parent if needed)
- for (byte sectLevel = LAYER_BEGINNING_OFFSET; sectLevel < numbersOfSectionLevels; sectLevel++) {
- final MovableGridRingList ringList = ringLists[sectLevel - LAYER_BEGINNING_OFFSET];
- final MovableGridRingList childRingList =
- sectLevel == LAYER_BEGINNING_OFFSET ? null : ringLists[sectLevel - LAYER_BEGINNING_OFFSET - 1];
- final MovableGridRingList parentRingList =
- sectLevel == numbersOfSectionLevels - 1 ? null : ringLists[sectLevel - LAYER_BEGINNING_OFFSET + 1];
- final byte f_sectLevel = sectLevel;
- ringList.forEachPosOrdered((section, pos) -> {
- if (f_sectLevel == LAYER_BEGINNING_OFFSET && section != null) {
- section.childCount = 0;
- //LOGGER.info("sect {} in first layer with non-null. Reset childCount", section.pos);
- }
- if (section != null && section.childCount != 0) {
- // Section will be in the unloaded state.
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} has child", section.pos);
- if (parentRingList != null) {
- LodRenderSection parent = _get(parentRingList, pos.x >> 1, pos.y >> 1);
- if (parent == null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} missing parent. Creating at {}", section.pos, section.pos.getParentPos());
- parent = _set(parentRingList, pos.x >> 1, pos.y >> 1, new LodRenderSection(section.pos.getParentPos()));
- parent.childCount++;
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("parent sect {} now has {} childs.", section.pos.getParentPos(), parent.childCount);
- }
- LodUtil.assertTrue(parent.childCount <= 4 && parent.childCount > 0);
- }
- for (byte i = 0; i < 4; i++) {
- DhSectionPos childPos = section.pos.getChildByIndex(i);
- LodUtil.assertTrue(childRingList != null);
- LodRenderSection child = _get(childRingList, childPos.sectionX, childPos.sectionZ);
- if (child == null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} missing child at {}. Creating.", section.pos, childPos);
- child = _set(childRingList, childPos.sectionX, childPos.sectionZ, new LodRenderSection(childPos));
- child.childCount = 0;
- } else if (child.childCount == -1) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} rescued child at {}.", section.pos, childPos);
- child.childCount = 0;
- }
- }
- section.childCount = 4;
- } else {
- final DhSectionPos sectPos = section != null ? section.pos : new DhSectionPos(f_sectLevel, pos.x, pos.y);
- LodUtil.assertTrue(sectPos.sectionDetailLevel == f_sectLevel
- && sectPos.sectionX == pos.x && sectPos.sectionZ == pos.y,
- "sectPos {} != {} @ {}", sectPos, pos, f_sectLevel);
-
- byte targetLevel = calculateExpectedDetailLevel(playerPos, sectPos);
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("0 child sect {}(null?{}) - target:{}/{} (parent:{})", sectPos, section == null,
- targetLevel, getLayerDataDetail(f_sectLevel),
- f_sectLevel == numbersOfSectionLevels-1 ? "N/A" : getLayerDataDetail((byte) (f_sectLevel+1)));
- if (f_sectLevel == numbersOfSectionLevels -1) {
- // Section is in the top level.
- if (targetLevel > getLayerDataDetail(f_sectLevel) && section != null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} in top & target>current. Mark as free.", sectPos);
- section.childCount = -1;
- }
- if (targetLevel <= getLayerDataDetail(f_sectLevel) && section == null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("null sect {} in top & target<=current. Creating.", sectPos);
- section = _set(ringList, pos.x, pos.y, new LodRenderSection(sectPos));
- }
- } else {
- // Section is not the top level. So we also need to consider the parent.
- if (targetLevel >= getLayerDataDetail((byte) (f_sectLevel+1)) && section != null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} target>=nextLevel. Mark as free.", sectPos);
- LodUtil.assertTrue(parentRingList != null);
- LodRenderSection parent = _getNotNull(parentRingList, pos.x >> 1, pos.y >> 1);
- LodUtil.assertTrue(parent.childCount <= 4 && parent.childCount > 0);
- parent.childCount--;
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("parent sect {} now has {} child.", sectPos, parent.childCount);
- section.childCount = -1;
- }
- if (targetLevel < getLayerDataDetail((byte) (f_sectLevel+1)) && section == null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("null sect {} target> 1, pos.y >> 1);
- if (parent == null) {
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("sect {} missing parent. Creating at {}", sectPos, sectPos.getParentPos());
- parent = _set(parentRingList, pos.x >> 1, pos.y >> 1, new LodRenderSection(sectPos.getParentPos()));
- }
- parent.childCount++;
- if (SUPER_VERBOSE_LOGGING) LOGGER.info("parent sect {} now has {} childs.", sectPos.getParentPos(), parent.childCount);
- }
- }
- }
- // Final quick assert to insure section pos is correct.
- if (section != null) {
- LodUtil.assertTrue(section.pos.sectionDetailLevel == f_sectLevel, "section.pos: " + section.pos + " vs level: " + f_sectLevel);
- LodUtil.assertTrue(section.pos.sectionX == pos.x, "section.pos: " + section.pos + " vs pos: " + pos);
- LodUtil.assertTrue(section.pos.sectionZ == pos.y, "section.pos: " + section.pos + " vs pos: " + pos);
- }
- });
- }
-
+ for (byte sectionDetailLevelIteration = TREE_LOWEST_DETAIL_LEVEL; sectionDetailLevelIteration < this.numbersOfSectionDetailLevels; sectionDetailLevelIteration++)
+ {
+ final byte sectionDetailLevel = sectionDetailLevelIteration; // final to prevent accidentally setting (and because intellij highlights final values different so it is easier to identify)
+
+ final MovableGridRingList ringList = this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL];
+
+ // child and parent are relative to the detail level
+ final MovableGridRingList childRingList = (sectionDetailLevel == TREE_LOWEST_DETAIL_LEVEL) ? null : this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL -1];
+ final MovableGridRingList parentRingList = (sectionDetailLevel == this.numbersOfSectionDetailLevels-1) ? null : this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL +1];
+
+
+ ringList.forEachPosOrdered((section, pos) ->
+ {
+ // TODO why do we need to use the halfPos to get sections?
+ final Pos2D halfPos = new Pos2D(BitShiftUtil.half(pos.x), BitShiftUtil.half(pos.y));
+
+
+ if (section != null && sectionDetailLevel == TREE_LOWEST_DETAIL_LEVEL)
+ {
+ // this section is a leaf node, set its children to 0
+ section.childCount = 0;
+ //LOGGER.info("sect {} in first layer with non-null. Reset childCount", section.pos);
+ }
+
+ if (section != null && section.childCount != 0)
+ {
+ // Section will be in the unloaded state, with 1-3 children
+ // load its parent and children
+ //TODO: Should I move this createChild steps to the Second tick pass?
+
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("sect "+section.pos+" has child");
+ }
+
+
+ // if this isn't the top detail level, make sure it has a valid parent
+ if (parentRingList != null)
+ {
+ LodRenderSection parentSection = this._getRenderSectionFromGridList(parentRingList, halfPos.x, halfPos.y);
+ if (parentSection == null)
+ {
+ // the parent render section is missing, create it
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("sect "+section.pos+" missing parent. Creating at "+section.pos.getParentPos());
+ }
+
+ parentSection = this._setRenderSectionInGridList(parentRingList, halfPos.x, halfPos.y, new LodRenderSection(section.pos.getParentPos()));
+ parentSection.childCount++;
+
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("parent sect "+section.pos.getParentPos()+" now has "+parentSection.childCount+" children.");
+ }
+ }
+
+ LodUtil.assertTrue(parentSection.childCount > 0 && parentSection.childCount <= 4);
+ }
+
+
+ // load this section's children
+ LodUtil.assertTrue(childRingList != null); // this shouldn't be a leaf node
+ for (int childIndex = 0; childIndex < 4; childIndex++)
+ {
+ DhSectionPos childPos = section.pos.getChildByIndex(childIndex);
+
+ LodRenderSection child = this._getRenderSectionFromGridList(childRingList, childPos.sectionX, childPos.sectionZ);
+ if (child == null)
+ {
+ // no child exists, create one
+
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("sect "+section.pos+" missing child at "+childPos+". Creating.");
+ }
+
+ child = this._setRenderSectionInGridList(childRingList, childPos.sectionX, childPos.sectionZ, new LodRenderSection(childPos));
+ child.childCount = 0;
+ }
+ else if (child.childCount == -1)
+ {
+ // a child existed but was marked for deletion,
+ // rescue (reuse) it
+
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("sect "+section.pos+" rescued child at "+childPos+".");
+ }
+
+ child.childCount = 0;
+ }
+ }
+
+ // the section is now fully loaded
+ section.childCount = 4;
+ }
+ else
+ {
+ // this render section is a fully loaded leaf node,
+ // TODO now what?
+
+ final DhSectionPos sectPos = (section != null) ? section.pos : new DhSectionPos(sectionDetailLevel, pos.x, pos.y);
+
+ // confirm the sectPos is correct
+ LodUtil.assertTrue(sectPos.sectionDetailLevel == sectionDetailLevel
+ && sectPos.sectionX == pos.x
+ && sectPos.sectionZ == pos.y,
+ "sectPos "+sectPos+" != "+pos+" @ "+sectionDetailLevel);
+
+
+ byte targetDetailLevel = this.calculateExpectedDetailLevel(playerPos, sectPos);
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ String layerDetailLevel = (sectionDetailLevel == this.numbersOfSectionDetailLevels-1) ? "N/A" : this.getLayerDataDetail((byte) (sectionDetailLevel+1))+"";
+ LOGGER.info("0 child sect "+sectPos+"(null?"+(section==null)+") - target:"+targetDetailLevel+"/"+this.getLayerDataDetail(sectionDetailLevel)+" (parent:"+layerDetailLevel+")");
+ }
+
+
+ if (sectionDetailLevel == this.numbersOfSectionDetailLevels-1) // TODO equivalent to ... == treeMaxDetailLevel
+ {
+ // Render Section is at the top detail level.
+
+ // TODO what does any of this mean? shouldn't both values in this if statement be independent of the section?
+ if (section != null && targetDetailLevel > this.getLayerDataDetail(sectionDetailLevel))
+ {
+ // this section is a higher detail level than we want, mark it for deletion
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("sect "+sectPos+" in top detail level & target>current. Mark as free.");
+ }
+ section.childCount = -1;
+ }
+
+ if (section == null && targetDetailLevel <= this.getLayerDataDetail(sectionDetailLevel))
+ {
+ // the render section for this detail level is missing, create it
+
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("null sect "+sectPos+" in top & target<=current. Creating.");
+ }
+ section = this._setRenderSectionInGridList(ringList, pos.x, pos.y, new LodRenderSection(sectPos));
+ }
+ }
+ else
+ {
+ // Section is not in the top detail level,
+ // so we also need to consider its parent.
+
+ if (section != null && targetDetailLevel >= this.getLayerDataDetail((byte) (sectionDetailLevel + 1)))
+ {
+ // this section is a higher detail level than what we want, mark it for deletion
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("sect "+sectPos+" target>=nextLevel. Mark as free.");
+ }
+ LodUtil.assertTrue(parentRingList != null);
+
+ LodRenderSection parent = this._getNotNull(parentRingList, halfPos.x, halfPos.y);
+ LodUtil.assertTrue(parent.childCount > 0 && parent.childCount <= 4);
+
+ parent.childCount--;
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("parent sect "+sectPos+" now has "+parent.childCount+" child.");
+ }
+
+ // mark this section for deletion
+ section.childCount = -1;
+
+ // Note that this doesn't necessarily mean this section will be freed as it may be rescued later
+ // due to neighboring quadrants not able to be freed (they pass targetLevel checks or has children)
+ // or due to parent's layer is in the Always Cascade mode. (containerType == null)
+ }
+
+
+ if (section == null && targetDetailLevel < this.getLayerDataDetail((byte) (sectionDetailLevel + 1)))
+ {
+ // the render section for this detail level is missing, create it
+
+ if (SUPER_VERBOSE_LOGGING)
+ {
+ LOGGER.info("null sect "+sectPos+" target= LAYER_BEGINNING_OFFSET; sectLevel--) {
- final MovableGridRingList ringList = ringLists[sectLevel - LAYER_BEGINNING_OFFSET];
- final MovableGridRingList childRingList =
- sectLevel == LAYER_BEGINNING_OFFSET ? null : ringLists[sectLevel - LAYER_BEGINNING_OFFSET - 1];
- final boolean doCascade = false; // TODO: Utilize this cascade mode or at least expose this option
- ringList.forEachPosOrdered((section, pos) -> {
- if (section == null) return;
-
- // Cascade layers
+ for (byte sectLevel = (byte) (this.numbersOfSectionDetailLevels - 1); sectLevel >= TREE_LOWEST_DETAIL_LEVEL; sectLevel--)
+ {
+ final MovableGridRingList ringList = this.renderSectionRingLists[sectLevel - TREE_LOWEST_DETAIL_LEVEL];
+ final MovableGridRingList childRingList = sectLevel == TREE_LOWEST_DETAIL_LEVEL ? null : this.renderSectionRingLists[sectLevel - TREE_LOWEST_DETAIL_LEVEL - 1];
+ final boolean doCascade = false; // TODO: Utilize this cascade mode or at least expose this option
+ ringList.forEachPosOrdered((section, pos) ->
+ {
+ if (section == null)
+ {
+ return;
+ }
+
+
+ // Cascade layers
// if (doCascade && section.childCount == 0) {
// LodUtil.assertTrue(childRingList != null);
-// // Create childs to cascade the layer.
+// // Create children to cascade the layer.
// for (byte i = 0; i < 4; i++) {
// DhSectionPos childPos = section.pos.getChild(i);
// LodRenderSection child = childRingList.get(childPos.sectionX, childPos.sectionZ);
@@ -399,71 +531,156 @@ public class LodQuadTree implements AutoCloseable {
// }
// section.childCount = 4;
// }
-
- // Call load on new sections, and tick on existing ones, and dispose old sections
- if (section.childCount == -1) {
- if (section.pos.sectionDetailLevel < numbersOfSectionLevels-1)
- LodUtil.assertTrue(getParentSection(section.pos).childCount == 0);
- ringList.set(pos.x, pos.y, null);
- section.dispose();
- return;
- } else {
- if (!section.isLoaded() && !section.isLoading()) {
- section.load(renderSourceProvider);
- } else if (section.isOutdated()) {
- section.reload(renderSourceProvider);
- }
- if (section.childCount == 4) section.disableRender();
- if (section.childCount == 0) section.enableRender(level, this);
- section.tick(this, level);
- }
-
- // Assertion steps
- LodUtil.assertTrue(section.childCount == 4 || section.childCount == 0);
- if (section.pos.sectionDetailLevel == LAYER_BEGINNING_OFFSET) LodUtil.assertTrue(section.childCount == 0);
- if (section.pos.sectionDetailLevel != LAYER_BEGINNING_OFFSET) {
- LodRenderSection child0 = getChildSection(section.pos, 0);
- LodRenderSection child1 = getChildSection(section.pos, 1);
- LodRenderSection child2 = getChildSection(section.pos, 2);
- LodRenderSection child3 = getChildSection(section.pos, 3);
- if (section.childCount == 4) LodUtil.assertTrue(
- child0 != null && child0.childCount != -1 &&
- child1 != null && child1.childCount != -1 &&
- child2 != null && child2.childCount != -1 &&
- child3 != null && child3.childCount != -1,
- "Sect {} child count 4 but child has null or is being disposed: {} {} {} {}",
- section.pos, child0, child1, child2, child3);
-
- if (section.childCount == 0) LodUtil.assertTrue(
- (child0 == null || child0.childCount == -1) &&
- (child1 == null || child1.childCount == -1) &&
- (child2 == null || child2.childCount == -1) &&
- (child3 == null || child3.childCount == -1),
- "Sect {} child count 0 but child is neither null or being disposed: {} {} {} {}",
- section.pos, child0, child1, child2, child3);
- }
- });
- }
- }
-
- public String getDebugString() {
- StringBuilder sb = new StringBuilder();
- for (byte i = 0; i < ringLists.length; i++) {
- sb.append("Layer ").append(i + LAYER_BEGINNING_OFFSET).append(":\n");
- sb.append(ringLists[i].toDetailString());
- sb.append("\n");
- sb.append("\n");
- }
- return sb.toString();
+
+ // Call load on new sections, tick on existing ones, and dispose old sections
+ if (section.childCount == -1)
+ {
+ // dispose the old section
+
+ if (section.pos.sectionDetailLevel < this.numbersOfSectionDetailLevels-1)
+ {
+ LodUtil.assertTrue(this.getParentSection(section.pos).childCount == 0);
+ }
+
+ ringList.remove(pos.x, pos.y);
+ section.dispose();
+ return;
+ }
+ else
+ {
+ if (!section.isLoaded() && !section.isLoading())
+ {
+ // load in the new section
+ section.load(this.renderSourceProvider);
+ }
+ else if (section.isOutdated())
+ {
+ // replace the out of date data
+ section.reload(this.renderSourceProvider);
+ }
+
+
+ // TODO is this right? - enable rendering if this section is a leaf node in the tree, otherwise disable rendering
+ if (section.childCount == 4)
+ {
+ section.disableRender();
+ }
+ if (section.childCount == 0)
+ {
+ section.enableRender(this.level, this);
+ }
+
+
+ // update the section
+ section.tick(this, this.level);
+ }
+
+
+
+ // section validation
+ LodUtil.assertTrue(section.childCount == 4 || section.childCount == 0);
+ if (section.pos.sectionDetailLevel == TREE_LOWEST_DETAIL_LEVEL)
+ {
+ // sections at the bottom of the tree (leaves) should have no additional children
+ LodUtil.assertTrue(section.childCount == 0);
+ }
+ else
+ {
+ LodRenderSection child0 = this.getChildSection(section.pos, 0);
+ LodRenderSection child1 = this.getChildSection(section.pos, 1);
+ LodRenderSection child2 = this.getChildSection(section.pos, 2);
+ LodRenderSection child3 = this.getChildSection(section.pos, 3);
+
+ if (section.childCount == 4)
+ {
+ LodUtil.assertTrue(
+ child0 != null && child0.childCount != -1 &&
+ child1 != null && child1.childCount != -1 &&
+ child2 != null && child2.childCount != -1 &&
+ child3 != null && child3.childCount != -1,
+ "Sect "+section.pos+" child count 4 but child has null or is being disposed: {} {} {} {}", child0, child1, child2, child3);
+ }
+ else if (section.childCount == 0)
+ {
+ LodUtil.assertTrue(
+ (child0 == null || child0.childCount == -1) &&
+ (child1 == null || child1.childCount == -1) &&
+ (child2 == null || child2.childCount == -1) &&
+ (child3 == null || child3.childCount == -1),
+ "Sect "+section.pos+" child count 0 but child is neither null or being disposed: {} {} {} {}",
+ child0, child1, child2, child3);
+ }
+ }
+ });
+ }
}
+
+
+
+
+
+ //=========================//
+ // internal helper methods //
+ //=========================//
+
+ private LodRenderSection _setRenderSectionInGridList(MovableGridRingList list, int x, int z, LodRenderSection renderSection)
+ {
+ LodUtil.assertTrue(renderSection != null, "setting null at [{},{}] in {}", x, z, list.toString());
+ LodUtil.assertTrue(renderSection.pos.sectionX == x && renderSection.pos.sectionZ == z, "pos {} != [{},{}] in {}", renderSection.pos, x, z, list.toString());
+
+ LodRenderSection section = list.setChained(x,z,renderSection);
+ LodUtil.assertTrue(section != null, "returned null at [{},{}]: {}", x, z, list.toString());
+ LodUtil.assertTrue(section == renderSection,"{} != {} in {}",section,renderSection, list.toString());
+ return section;
+ }
+ private LodRenderSection _getNotNull(MovableGridRingList list, int x, int z)
+ {
+ LodUtil.assertTrue(list.inRange(x,z), "[{},{}] not in range of {}", x, z, list.toString());
+
+ LodRenderSection section = list.get(x,z);
+ LodUtil.assertTrue(section != null, "getting null at [{},{}] in {}", x, z, list.toString());
+ LodUtil.assertTrue(section.pos.sectionX == x && section.pos.sectionZ == z, "obj {} != [{},{}] in {}", section, x, z, list.toString());
+ return section;
+ }
+ private LodRenderSection _getRenderSectionFromGridList(MovableGridRingList list, int x, int z)
+ {
+ LodRenderSection section = list.get(x,z);
+ LodUtil.assertTrue(section == null || (section.pos.sectionX == x && section.pos.sectionZ == z), "obj {} != [{},{}] in {}", section, x, z, list.toString());
+ return section;
+ }
+
+
+
+ //==============//
+ // base methods //
+ //==============//
+
+ public String getDebugString()
+ {
+ StringBuilder sb = new StringBuilder();
+ for (byte i = 0; i < this.renderSectionRingLists.length; i++)
+ {
+ sb.append("Layer ").append(i + TREE_LOWEST_DETAIL_LEVEL).append(":\n");
+ sb.append(this.renderSectionRingLists[i].toDetailString());
+ sb.append("\n");
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
@Override
- public void close() {
- for (MovableGridRingList ringList : ringLists) {
- ringList.forEach((section) -> {
- if (section != null) section.dispose();
- });
- }
-
- }
+ public void close()
+ {
+ for (MovableGridRingList ringList : this.renderSectionRingLists)
+ {
+ ringList.forEach((section) ->
+ {
+ if (section != null)
+ {
+ section.dispose();
+ }
+ });
+ }
+ }
+
}
diff --git a/core/src/main/java/com/seibel/lod/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/lod/core/render/LodRenderSection.java
index 9d67b0427..68a072252 100644
--- a/core/src/main/java/com/seibel/lod/core/render/LodRenderSection.java
+++ b/core/src/main/java/com/seibel/lod/core/render/LodRenderSection.java
@@ -12,66 +12,108 @@ import java.util.concurrent.CompletableFuture;
public class LodRenderSection
{
public final DhSectionPos pos;
-
+
+ // TODO create an enum to represent the section's state instead of using magic numbers in the childCount
+ // states: loaded (childCount 4), unloaded (childCount 0), markedForDeletion/markedForFreeing (childCount -1)
+
/* Following used for LodQuadTree tick() method, and ONLY for that method! */
// the number of children of this section
// (Should always be 4 after tick() is done, or 0 only if this is an unloaded node)
public byte childCount = 0;
-
- // TODO: Should I provide a way to change the render source?
- private ILodRenderSource lodRenderSource;
+
private CompletableFuture loadFuture;
private boolean isRenderEnabled = false;
- private IRenderSourceProvider provider = null;
-
- // Create sub region
- public LodRenderSection(DhSectionPos pos) {
- this.pos = pos;
+
+ // TODO: Should I provide a way to change the render source?
+ private ILodRenderSource lodRenderSource;
+ private IRenderSourceProvider renderSourceProvider = null; // TODO: rename these two interfaces to make it more obvious what each one does
+
+ private EVerticalQuality previousVerticalQualitySetting = null;
+
+
+
+ // Create sub region
+ public LodRenderSection(DhSectionPos pos) { this.pos = pos; }
+
+
+
+ //===========//
+ // rendering //
+ //===========//
+
+ public void enableRender(IDhClientLevel level, LodQuadTree quadTree)
+ {
+ if (this.isRenderEnabled)
+ {
+ return;
+ }
+
+
+ this.loadFuture = this.renderSourceProvider.read(this.pos);
+ this.isRenderEnabled = true;
}
-
- public void enableRender(IDhClientLevel level, LodQuadTree quadTree) {
- if (isRenderEnabled) return;
- loadFuture = provider.read(pos);
- isRenderEnabled = true;
- }
- public void disableRender() {
- if (!isRenderEnabled) return;
- if (lodRenderSource != null) {
- lodRenderSource.disableRender();
- lodRenderSource.dispose();
- lodRenderSource = null;
+ public void disableRender()
+ {
+ if (!this.isRenderEnabled)
+ {
+ return;
+ }
+
+
+ if (this.lodRenderSource != null)
+ {
+ this.lodRenderSource.disableRender();
+ this.lodRenderSource.dispose();
+ this.lodRenderSource = null;
}
- if (loadFuture != null) {
- loadFuture.cancel(true);
- loadFuture = null;
+ if (this.loadFuture != null)
+ {
+ this.loadFuture.cancel(true);
+ this.loadFuture = null;
}
- isRenderEnabled = false;
+
+ this.isRenderEnabled = false;
}
-
+
+
+
+ //
+ //
+ //
+
public void load(IRenderSourceProvider renderDataProvider)
{
- provider = renderDataProvider;
- this.previousQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
+ this.renderSourceProvider = renderDataProvider;
+ this.previousVerticalQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
}
- public void reload(IRenderSourceProvider renderDataProvider) {
- if (loadFuture != null) {
- loadFuture.cancel(true);
- loadFuture = null;
+ public void reload(IRenderSourceProvider renderDataProvider)
+ {
+ if (this.loadFuture != null)
+ {
+ this.loadFuture.cancel(true);
+ this.loadFuture = null;
}
- if (lodRenderSource != null) {
- lodRenderSource.dispose();
- lodRenderSource = null;
+
+ if (this.lodRenderSource != null)
+ {
+ this.lodRenderSource.dispose();
+ this.lodRenderSource = null;
}
- loadFuture = renderDataProvider.read(pos);
- this.previousQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
+
+ this.loadFuture = renderDataProvider.read(this.pos);
+ this.previousVerticalQualitySetting = Config.Client.Graphics.Quality.verticalQuality.get();
}
-
+
+
+
+
public void tick(LodQuadTree quadTree, IDhClientLevel level)
{
if (this.loadFuture != null && this.loadFuture.isDone())
{
this.lodRenderSource = this.loadFuture.join();
this.loadFuture = null;
+
if (this.isRenderEnabled)
{
this.lodRenderSource.enableRender(level, quadTree);
@@ -80,54 +122,49 @@ public class LodRenderSection
if (this.lodRenderSource != null)
{
- this.provider.refreshRenderSource(this.lodRenderSource);
+ this.renderSourceProvider.refreshRenderSource(this.lodRenderSource);
}
}
-
- public void dispose() {
- if (lodRenderSource != null) {
- lodRenderSource.dispose();
- } else if (loadFuture != null) {
- loadFuture.cancel(true);
- }
- }
-
- public boolean canRender() {
- return isLoaded() && isRenderEnabled && lodRenderSource != null;
- }
-
- public boolean isLoaded() {
- return provider != null;
- }
-
- //FIXME: Used by RenderBufferHandler
- public int FIXME_BYPASS_DONT_USE_getChildCount() {
- return childCount;
- }
-
- public boolean isLoading() {
- return false;
- }
- private EVerticalQuality previousQualitySetting = null;
- public boolean isOutdated()
+ public void dispose()
{
- return this.previousQualitySetting != Config.Client.Graphics.Quality.verticalQuality.get() || (lodRenderSource != null && !lodRenderSource.isValid());
- }
+ if (this.lodRenderSource != null)
+ {
+ this.lodRenderSource.dispose();
+ }
+ else if (this.loadFuture != null)
+ {
+ this.loadFuture.cancel(true);
+ }
+ }
- public ILodRenderSource getRenderSource() {
- return lodRenderSource;
- }
+
+
+ public boolean canRender() { return this.isLoaded() && this.isRenderEnabled && this.lodRenderSource != null; }
+ public boolean isLoaded() { return this.renderSourceProvider != null; }
+ public boolean isLoading() { return false; }
+
+ //FIXME: Used by RenderBufferHandler
+ public int FIXME_BYPASS_DONT_USE_getChildCount() { return this.childCount; }
+
+ public boolean isOutdated() { return this.previousVerticalQualitySetting != Config.Client.Graphics.Quality.verticalQuality.get() || (this.lodRenderSource != null && !this.lodRenderSource.isValid()); }
+ public ILodRenderSource getRenderSource() { return this.lodRenderSource; }
+
+
+
+
+
public String toString() {
return "LodRenderSection{" +
- "pos=" + pos +
- ", childCount=" + childCount +
- ", lodRenderSource=" + lodRenderSource +
- ", loadFuture=" + loadFuture +
- ", isRenderEnabled=" + isRenderEnabled +
+ "pos=" + this.pos +
+ ", childCount=" + this.childCount +
+ ", lodRenderSource=" + this.lodRenderSource +
+ ", loadFuture=" + this.loadFuture +
+ ", isRenderEnabled=" + this.isRenderEnabled +
'}';
}
+
}
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 cf87d64a0..f7bb3620e 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
@@ -194,7 +194,7 @@ public class RenderBufferHandler {
public RenderBufferHandler(LodQuadTree target) {
this.target = target;
- MovableGridRingList referenceList = target.getRingList((byte) (target.getNumbersOfSectionLevels() - 1));
+ MovableGridRingList referenceList = target.getRingList((byte) (target.getNumbersOfSectionDetailLevels() - 1));
Pos2D center = referenceList.getCenter();
renderBufferNodes = new MovableGridRingList<>(referenceList.getHalfSize(), center);
}
@@ -215,7 +215,7 @@ public class RenderBufferHandler {
}
public void update() {
- byte topDetail = (byte) (target.getNumbersOfSectionLevels() - 1);
+ byte topDetail = (byte) (target.getNumbersOfSectionDetailLevels() - 1);
MovableGridRingList referenceList = target.getRingList(topDetail);
Pos2D center = referenceList.getCenter();
//boolean moved = renderBufferNodes.getCenterBlockPos().x != center.x || renderBufferNodes.getCenterBlockPos().y != center.y;
diff --git a/core/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java b/core/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java
index 3d603b356..8e8834eb4 100644
--- a/core/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java
+++ b/core/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java
@@ -358,7 +358,7 @@ public class MovableGridRingList extends ArrayList implements List
/**
- * TODO: Use MutablePos2D in the future
+ * TODO: Use MutablePos2D in the future
* Will pass in null entries
*/
public void forEachPos(BiConsumer super T, Pos2D> consumer)
@@ -383,7 +383,7 @@ public class MovableGridRingList extends ArrayList implements List
}
/**
- * TODO: Use MutablePos2D in the future
+ * TODO: Use MutablePos2D in the future
* Will skip null entries
*/
public void forEachOrdered(Consumer super T> consumer)
@@ -414,7 +414,7 @@ public class MovableGridRingList extends ArrayList implements List
}
/**
- * TODO: Use MutablePos2D in the future
+ * TODO: Use MutablePos2D in the future
* Will pass in null entries
*/
public void forEachPosOrdered(BiConsumer super T, Pos2D> consumer)
@@ -432,8 +432,8 @@ public class MovableGridRingList extends ArrayList implements List
for (Pos2D offset : this.ringIteratorList)
{
LodUtil.assertTrue(this._inRangeAcquired(min.x + offset.x, min.y + offset.y, min));
- T t = this._getUnsafe(min.x + offset.x, min.y + offset.y);
- consumer.accept(t, new Pos2D(min.x + offset.x, min.y + offset.y));
+ T item = this._getUnsafe(min.x + offset.x, min.y + offset.y);
+ consumer.accept(item, new Pos2D(min.x + offset.x, min.y + offset.y));
}
}
finally
diff --git a/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java
index f76b04518..624c13560 100644
--- a/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java
+++ b/core/src/main/java/com/seibel/lod/core/world/DhClientWorld.java
@@ -63,11 +63,11 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
}
private void _clientTick() {
- int newViewDistance = Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16;
+ int newViewDistance = Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
Iterator iterator = levels.values().iterator();
while (iterator.hasNext()) {
DhClientLevel level = iterator.next();
- if (level.tree.viewDistance != newViewDistance) {
+ if (level.tree.blockViewDistance != newViewDistance) {
level.close();
iterator.remove();
}