Refactor LodQuadTree
This commit is contained in:
@@ -32,6 +32,15 @@ public class BitShiftUtil
|
||||
*/
|
||||
public static int half(int value) { return value >> 1; }
|
||||
|
||||
/**
|
||||
* Equivalent to: <br>
|
||||
* value >> power, <br>
|
||||
* value / 2^power <br><br>
|
||||
*
|
||||
* Note: value / 2^power isn't identical for negative values
|
||||
*/
|
||||
public static int divideByPowerOfTwo(int value, int power) { return value >> power; }
|
||||
|
||||
/**
|
||||
* Equivalent to: <br>
|
||||
* value << 1, <br>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<WeakReference<Message>> selfUpdateMessages = new LinkedList<>();
|
||||
public static void addStringToDisplay(List<String> list) {
|
||||
list.addAll(Arrays.asList(DEFAULT_STR));
|
||||
Iterator<WeakReference<Message>> it = selfUpdateMessages.iterator();
|
||||
while (it.hasNext()) {
|
||||
WeakReference<Message> 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<String> 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<String> output) {
|
||||
output.addAll(Arrays.asList(lines));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class DynamicMessage extends Message {
|
||||
private final Supplier<String> supplier;
|
||||
public DynamicMessage(Supplier<String> message) {
|
||||
this.supplier = message;
|
||||
}
|
||||
public void printTo(List<String> list) {
|
||||
String msg = supplier.get();
|
||||
if (msg != null) {
|
||||
list.add(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public static class MultiDynamicMessage extends Message {
|
||||
private final Supplier<String>[] supplier;
|
||||
@SafeVarargs
|
||||
public MultiDynamicMessage(Supplier<String>... messages) {
|
||||
this.supplier = messages;
|
||||
}
|
||||
public void printTo(List<String> list) {
|
||||
for (Supplier<String> s : supplier) {
|
||||
String msg = s.get();
|
||||
if (msg != null) {
|
||||
list.add(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NestedMessage extends Message {
|
||||
private final Supplier<String[]> supplier;
|
||||
public NestedMessage(Supplier<String[]> message) {
|
||||
this.supplier = message;
|
||||
}
|
||||
public void printTo(List<String> 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<WeakReference<Message>> selfUpdateMessages = new LinkedList<>();
|
||||
|
||||
public static void addStringToDisplay(List<String> list)
|
||||
{
|
||||
list.addAll(Arrays.asList(DEFAULT_STR));
|
||||
Iterator<WeakReference<Message>> iterator = selfUpdateMessages.iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
WeakReference<Message> 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<String> 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<String> output)
|
||||
{
|
||||
output.addAll(Arrays.asList(lines));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class DynamicMessage extends Message
|
||||
{
|
||||
private final Supplier<String> supplier;
|
||||
|
||||
public DynamicMessage(Supplier<String> message)
|
||||
{
|
||||
this.supplier = message;
|
||||
}
|
||||
|
||||
public void printTo(List<String> list)
|
||||
{
|
||||
String msg = supplier.get();
|
||||
if (msg != null)
|
||||
{
|
||||
list.add(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class MultiDynamicMessage extends Message
|
||||
{
|
||||
private final Supplier<String>[] supplier;
|
||||
|
||||
@SafeVarargs
|
||||
public MultiDynamicMessage(Supplier<String>... messages)
|
||||
{
|
||||
this.supplier = messages;
|
||||
}
|
||||
|
||||
public void printTo(List<String> list)
|
||||
{
|
||||
for (Supplier<String> s : supplier)
|
||||
{
|
||||
String msg = s.get();
|
||||
if (msg != null)
|
||||
{
|
||||
list.add(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NestedMessage extends Message
|
||||
{
|
||||
private final Supplier<String[]> supplier;
|
||||
|
||||
public NestedMessage(Supplier<String[]> message)
|
||||
{
|
||||
this.supplier = message;
|
||||
}
|
||||
|
||||
public void printTo(List<String> list)
|
||||
{
|
||||
String[] msg = supplier.get();
|
||||
if (msg != null)
|
||||
{
|
||||
list.addAll(Arrays.asList(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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. <br><br>
|
||||
*
|
||||
* This class represent a circular quadTree of lodSections. <br>
|
||||
* Each section at level n is populated in one or more ways: <br>
|
||||
* -by constructing it from the data of all the children sections (lower levels) <br>
|
||||
* -by loading from file <br>
|
||||
* -by adding data with the lodBuilder <br>
|
||||
* <br><br>
|
||||
* 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<LodRenderSection>[] 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<LodRenderSection>[] 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<LodRenderSection> getRingList(byte detailLevel) {
|
||||
return ringLists[detailLevel - LAYER_BEGINNING_OFFSET];
|
||||
}
|
||||
|
||||
public MovableGridRingList<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> ringList = ringLists[sectLevel - LAYER_BEGINNING_OFFSET];
|
||||
final MovableGridRingList<LodRenderSection> childRingList =
|
||||
sectLevel == LAYER_BEGINNING_OFFSET ? null : ringLists[sectLevel - LAYER_BEGINNING_OFFSET - 1];
|
||||
final MovableGridRingList<LodRenderSection> 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<nextLevel. Creating.", sectPos);
|
||||
section = _set(ringList, pos.x, pos.y, new LodRenderSection(sectPos));
|
||||
LodUtil.assertTrue(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 {}", 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<LodRenderSection> ringList = this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL];
|
||||
|
||||
// child and parent are relative to the detail level
|
||||
final MovableGridRingList<LodRenderSection> childRingList = (sectionDetailLevel == TREE_LOWEST_DETAIL_LEVEL) ? null : this.renderSectionRingLists[sectionDetailLevel- TREE_LOWEST_DETAIL_LEVEL -1];
|
||||
final MovableGridRingList<LodRenderSection> 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<nextLevel. Creating.");
|
||||
}
|
||||
|
||||
section = this._setRenderSectionInGridList(ringList, pos.x, pos.y, new LodRenderSection(sectPos));
|
||||
LodUtil.assertTrue(parentRingList != null);
|
||||
|
||||
|
||||
LodRenderSection parent = this._getRenderSectionFromGridList(parentRingList, halfPos.x, halfPos.y);
|
||||
if (parent == null)
|
||||
{
|
||||
// this render section's parent is missing, create it
|
||||
|
||||
if (SUPER_VERBOSE_LOGGING)
|
||||
{
|
||||
LOGGER.info("sect "+sectPos+" missing parent. Creating at "+sectPos.getParentPos());
|
||||
}
|
||||
|
||||
parent = this._setRenderSectionInGridList(parentRingList, halfPos.x, halfPos.y, new LodRenderSection(sectPos.getParentPos()));
|
||||
}
|
||||
|
||||
|
||||
parent.childCount++;
|
||||
if (SUPER_VERBOSE_LOGGING)
|
||||
{
|
||||
LOGGER.info("parent render section "+sectPos.getParentPos()+" now has "+parent.childCount+" children.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Final quick assert to insure section pos is correct.
|
||||
if (section != null)
|
||||
{
|
||||
LodUtil.assertTrue(section.pos.sectionDetailLevel == sectionDetailLevel, "section.pos: " + section.pos + " vs level: " + sectionDetailLevel);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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)
|
||||
// Second tick pass:
|
||||
// Cascade the layers that is in Always Cascade Mode from top to bottom. (Not yet exposed or used)
|
||||
// At the same time, load and unload sections (and can also be used to assert everything is working). Step:
|
||||
// ===Assertion steps===
|
||||
// At the same time, load and unload sections (and can also be used to assert everything is working).
|
||||
//
|
||||
// // ===Assertion steps===
|
||||
// assert childCount == 4 || childCount == 0 || childCount == -1
|
||||
// if childCount == 4 assert all children exist
|
||||
// if childCount == 0 assert all children are null
|
||||
// if childCount == -1 assert parent childCount is 0
|
||||
// // ======================
|
||||
//
|
||||
// if childCount == 4 && section is loaded:
|
||||
// - unload section
|
||||
// if childCount == 0 && section is unloaded:
|
||||
// - load section
|
||||
// if childCount == -1: // (section can be loaded or unloaded, due to fast movement)
|
||||
// if childCount == -1: // (section could be loaded or unloaded if the player is moving fast)
|
||||
// - set this section to null (TODO: Is this needed to be first or last or don't matter for concurrency?)
|
||||
// - If loaded unload section
|
||||
for (byte sectLevel = (byte) (numbersOfSectionLevels - 1); sectLevel >= LAYER_BEGINNING_OFFSET; sectLevel--) {
|
||||
final MovableGridRingList<LodRenderSection> ringList = ringLists[sectLevel - LAYER_BEGINNING_OFFSET];
|
||||
final MovableGridRingList<LodRenderSection> 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<LodRenderSection> ringList = this.renderSectionRingLists[sectLevel - TREE_LOWEST_DETAIL_LEVEL];
|
||||
final MovableGridRingList<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> 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<LodRenderSection> ringList : ringLists) {
|
||||
ringList.forEach((section) -> {
|
||||
if (section != null) section.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
public void close()
|
||||
{
|
||||
for (MovableGridRingList<LodRenderSection> ringList : this.renderSectionRingLists)
|
||||
{
|
||||
ringList.forEach((section) ->
|
||||
{
|
||||
if (section != null)
|
||||
{
|
||||
section.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ILodRenderSource> 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 +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ public class RenderBufferHandler {
|
||||
|
||||
public RenderBufferHandler(LodQuadTree target) {
|
||||
this.target = target;
|
||||
MovableGridRingList<LodRenderSection> referenceList = target.getRingList((byte) (target.getNumbersOfSectionLevels() - 1));
|
||||
MovableGridRingList<LodRenderSection> 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<LodRenderSection> referenceList = target.getRingList(topDetail);
|
||||
Pos2D center = referenceList.getCenter();
|
||||
//boolean moved = renderBufferNodes.getCenterBlockPos().x != center.x || renderBufferNodes.getCenterBlockPos().y != center.y;
|
||||
|
||||
@@ -358,7 +358,7 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T>
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Use MutablePos2D in the future
|
||||
* TODO: Use MutablePos2D in the future <br>
|
||||
* Will pass in null entries
|
||||
*/
|
||||
public void forEachPos(BiConsumer<? super T, Pos2D> consumer)
|
||||
@@ -383,7 +383,7 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Use MutablePos2D in the future
|
||||
* TODO: Use MutablePos2D in the future <br>
|
||||
* Will skip null entries
|
||||
*/
|
||||
public void forEachOrdered(Consumer<? super T> consumer)
|
||||
@@ -414,7 +414,7 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Use MutablePos2D in the future
|
||||
* TODO: Use MutablePos2D in the future <br>
|
||||
* Will pass in null entries
|
||||
*/
|
||||
public void forEachPosOrdered(BiConsumer<? super T, Pos2D> consumer)
|
||||
@@ -432,8 +432,8 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T>
|
||||
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
|
||||
|
||||
@@ -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<DhClientLevel> 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user