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 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 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 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(); }