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
new file mode 100644
index 000000000..4dde773e0
--- /dev/null
+++ b/api/src/main/java/com/seibel/lod/core/util/BitShiftUtil.java
@@ -0,0 +1,35 @@
+package com.seibel.lod.core.util;
+
+/**
+ * A list of helper methods to make code easier to read.
+ * Specifically written because bit shifts short circuit James' brain.
+ *
+ * @author James Seibel
+ * @version 2022-11-6
+ */
+public class BitShiftUtil
+{
+ /**
+ * Equivalent to:
+ * 1 << value,
+ * 2^value,
+ * Math.pow(2, value)
+ *
+ * Note: Math.pow() isn't identical for large values where bits would be lost in the shift, however for medium to small values they function the same.
+ *
+ * Can also be used to replace bit shifts in the format:
+ * multiplier << value;
+ * multiplier * powerOfTwo(value);
+ */
+ public static int powerOfTwo(int value) { return 1 << value; }
+
+ /**
+ * Equivalent to:
+ * value >> 1,
+ * value / 2
+ *
+ * Note: value / 2 isn't identical for negative values
+ */
+ public static int half(int value) { return value >> 1; }
+
+}
diff --git a/api/src/main/java/com/seibel/lod/core/util/MathUtil.java b/api/src/main/java/com/seibel/lod/core/util/MathUtil.java
index d232be9fc..1f4df9af3 100644
--- a/api/src/main/java/com/seibel/lod/core/util/MathUtil.java
+++ b/api/src/main/java/com/seibel/lod/core/util/MathUtil.java
@@ -6,43 +6,28 @@ public class MathUtil
* Clamps the given value between the min and max values.
* May behave strangely if min > max.
*/
- public static int clamp(int min, int value, int max)
- {
- return Math.min(max, Math.max(value, min));
- }
+ public static int clamp(int min, int value, int max) { return Math.min(max, Math.max(value, min)); }
/**
* Clamps the given value between the min and max values.
* May behave strangely if min > max.
*/
- public static float clamp(float min, float value, float max)
- {
- return Math.min(max, Math.max(value, min));
- }
+ public static float clamp(float min, float value, float max) { return Math.min(max, Math.max(value, min)); }
/**
* Clamps the given value between the min and max values.
* May behave strangely if min > max.
*/
- public static double clamp(double min, double value, double max)
- {
- return Math.min(max, Math.max(value, min));
- }
+ public static double clamp(double min, double value, double max) { return Math.min(max, Math.max(value, min)); }
/**
* Like Math.floorDiv, but reverse in that it is a ceilDiv
*/
- public static int ceilDiv(int value, int divider) {
- return -Math.floorDiv(-value, divider);
- }
+ public static int ceilDiv(int value, int divider) { return -Math.floorDiv(-value, divider); }
// Why is this not in the standard library?! Come on Java!
- public static byte min(byte a, byte b) {
- return a < b ? a : b;
- }
- public static byte max(byte a, byte b) {
- return a > b ? a : b;
- }
+ public static byte min(byte a, byte b) { return a < b ? a : b; }
+ public static byte max(byte a, byte b) { return a > b ? a : b; }
/** This is copied from Minecraft's MathHelper class */
@@ -54,11 +39,10 @@ public class MathUtil
numb = Float.intBitsToFloat(i);
return numb * (1.5F - half * numb * numb);
}
- public static float pow2(float x) {return x*x;}
- public static double pow2(double x) {return x*x;}
- public static int pow2(int x) {return x*x;}
-
- public static long pow2(long x) {return x*x;}
+ public static float pow2(float x) { return x * x; }
+ public static double pow2(double x) { return x * x; }
+ public static int pow2(int x) { return x * x; }
+ public static long pow2(long x) { return x * x; }
}
diff --git a/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java b/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java
index b8d855c09..24eb191f2 100644
--- a/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java
+++ b/core/src/main/java/com/seibel/lod/core/datatype/column/ColumnRenderSource.java
@@ -374,7 +374,7 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
ColumnRenderSource[] data = new ColumnRenderSource[ELodDirection.ADJ_DIRECTIONS.length];
for (ELodDirection direction : ELodDirection.ADJ_DIRECTIONS)
{
- LodRenderSection section = quadTree.getSection(this.sectionPos.getAdjacent(direction)); //FIXME: Handle traveling through different detail levels
+ LodRenderSection section = quadTree.getSection(this.sectionPos.getAdjacentPos(direction)); //FIXME: Handle traveling through different detail levels
if (section != null && section.getRenderSource() != null && section.getRenderSource() instanceof ColumnRenderSource)
{
data[direction.ordinal() - 2] = ((ColumnRenderSource) section.getRenderSource());
@@ -503,7 +503,7 @@ public class ColumnRenderSource implements ILodRenderSource, IColumnDatatype
stringBuilder.append(sectionPos);
stringBuilder.append(LINE_DELIMITER);
- int size = sectionPos.getWidth().value;
+ int size = sectionPos.getWidth().numberOfLodSectionsWide;
for (int z = 0; z < size; z++)
{
for (int x = 0; x < size; x++)
diff --git a/core/src/main/java/com/seibel/lod/core/datatype/full/SparseDataSource.java b/core/src/main/java/com/seibel/lod/core/datatype/full/SparseDataSource.java
index 6f6328399..2ea7b9e31 100644
--- a/core/src/main/java/com/seibel/lod/core/datatype/full/SparseDataSource.java
+++ b/core/src/main/java/com/seibel/lod/core/datatype/full/SparseDataSource.java
@@ -156,7 +156,7 @@ public class SparseDataSource implements IIncompleteDataSource
DhLodPos basePos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
DhLodPos dataPos = pos.getCorner(SPARSE_UNIT_DETAIL);
- int coveredChunks = pos.getWidth(SPARSE_UNIT_DETAIL).value;
+ int coveredChunks = pos.getWidth(SPARSE_UNIT_DETAIL).numberOfLodSectionsWide;
int sourceDataPerChunk = SPARSE_UNIT_SIZE >>> fullSource.getDataDetail();
LodUtil.assertTrue(coveredChunks*sourceDataPerChunk == FullDataSource.SECTION_SIZE);
int offsetX = dataPos.x-basePos.x;
diff --git a/core/src/main/java/com/seibel/lod/core/datatype/full/SpottyDataSource.java b/core/src/main/java/com/seibel/lod/core/datatype/full/SpottyDataSource.java
index 6c0377ae9..af6782999 100644
--- a/core/src/main/java/com/seibel/lod/core/datatype/full/SpottyDataSource.java
+++ b/core/src/main/java/com/seibel/lod/core/datatype/full/SpottyDataSource.java
@@ -214,7 +214,7 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
int offsetZ = dataPos.z - basePos.z;
LodUtil.assertTrue(offsetX >= 0 && offsetX < SECTION_SIZE && offsetZ >= 0 && offsetZ < SECTION_SIZE);
int chunksPerData = 1 << (getDataDetail() - SparseDataSource.SPARSE_UNIT_DETAIL);
- int dataSpan = sectionPos.getWidth(getDataDetail()).value;
+ int dataSpan = sectionPos.getWidth(getDataDetail()).numberOfLodSectionsWide;
for (int ox = 0; ox < dataSpan; ox++) {
for (int oz = 0; oz < dataSpan; oz++) {
@@ -229,7 +229,7 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
}
} else {
DhLodPos dataPos = pos.getSectionBBoxPos();
- int lowerSectionsPerData = sectionPos.getWidth(dataPos.detail).value;
+ int lowerSectionsPerData = sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide;
if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) return;
DhLodPos basePos = sectionPos.getCorner(getDataDetail());
@@ -254,7 +254,7 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
DhLodPos dataPos = pos.getCorner(getDataDetail());
int offsetX = dataPos.x - basePos.x;
int offsetZ = dataPos.z - basePos.z;
- int dataSpan = sectionPos.getWidth(getDataDetail()).value;
+ int dataSpan = sectionPos.getWidth(getDataDetail()).numberOfLodSectionsWide;
for (int ox = 0; ox < dataSpan; ox++) {
for (int oz = 0; oz < dataSpan; oz++) {
isColumnNotEmpty.set((offsetX + ox) * SECTION_SIZE + offsetZ + oz, true);
@@ -262,7 +262,7 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
}
} else {
DhLodPos dataPos = pos.getSectionBBoxPos();
- int lowerSectionsPerData = sectionPos.getWidth(dataPos.detail).value;
+ int lowerSectionsPerData = sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide;
if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) return;
DhLodPos basePos = sectionPos.getCorner(getDataDetail());
dataPos = dataPos.convertUpwardsTo(getDataDetail());
diff --git a/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java b/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java
index 82f2390a4..2ec83b920 100644
--- a/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java
+++ b/core/src/main/java/com/seibel/lod/core/datatype/transform/FullToColumnTransformer.java
@@ -37,8 +37,8 @@ public class FullToColumnTransformer {
if (dataDetail == columnSource.getDataDetail()) {
int baseX = pos.getCorner().getCorner().x;
int baseZ = pos.getCorner().getCorner().z;
- for (int x = 0; x < pos.getWidth(dataDetail).value; x++) {
- for (int z = 0; z < pos.getWidth(dataDetail).value; z++) {
+ for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++) {
+ for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++) {
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
SingleFullArrayView fullArrayView = data.get(x, z);
convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1);
@@ -77,8 +77,8 @@ public class FullToColumnTransformer {
if (dataDetail == columnSource.getDataDetail()) {
int baseX = pos.getCorner().getCorner().x;
int baseZ = pos.getCorner().getCorner().z;
- for (int x = 0; x < pos.getWidth(dataDetail).value; x++) {
- for (int z = 0; z < pos.getWidth(dataDetail).value; z++) {
+ for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++) {
+ for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++) {
SingleFullArrayView fullArrayView = data.tryGet(x, z);
if (fullArrayView == null) continue;
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
diff --git a/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java
index 34142d9fe..f08a20f53 100644
--- a/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java
+++ b/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java
@@ -136,7 +136,7 @@ public class DataFileHandler implements IDataSourceProvider {
outerLoop:
while (--detail >= minDetailLevel) {
DhLodPos min = pos.getCorner().getCorner(detail);
- int count = pos.getSectionBBoxPos().getWidth(detail);
+ int count = pos.getSectionBBoxPos().getBlockWidth(detail);
for (int ox = 0; ox ColumnRenderSource.SECTION_SIZE_OFFSET) {
- recursive_write(sectPos.getChild(0), chunkData);
- recursive_write(sectPos.getChild(1), chunkData);
- recursive_write(sectPos.getChild(2), chunkData);
- recursive_write(sectPos.getChild(3), chunkData);
+ recursive_write(sectPos.getChildByIndex(0), chunkData);
+ recursive_write(sectPos.getChildByIndex(1), chunkData);
+ recursive_write(sectPos.getChildByIndex(2), chunkData);
+ recursive_write(sectPos.getChildByIndex(3), chunkData);
}
RenderMetaFile metaFile = files.get(sectPos);
if (metaFile != null) { // Fast path: if there is a file for this section, just write to it.
diff --git a/core/src/main/java/com/seibel/lod/core/generation/GenerationQueue.java b/core/src/main/java/com/seibel/lod/core/generation/GenerationQueue.java
index 63d685cef..cf57342b1 100644
--- a/core/src/main/java/com/seibel/lod/core/generation/GenerationQueue.java
+++ b/core/src/main/java/com/seibel/lod/core/generation/GenerationQueue.java
@@ -4,7 +4,6 @@ import com.seibel.lod.core.datatype.full.ChunkSizedData;
import com.seibel.lod.core.pos.DhBlockPos2D;
import com.seibel.lod.core.pos.DhLodPos;
import com.seibel.lod.core.pos.Pos2D;
-import com.seibel.lod.core.util.MathUtil;
import com.seibel.lod.core.util.objects.UncheckedInterruptedException;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.pos.DhChunkPos;
@@ -120,7 +119,7 @@ public class GenerationQueue implements Closeable {
// FIXME: This is using up a TONS of time to process!
private final ConcurrentSkipListMap taskGroups = new ConcurrentSkipListMap<>(
(a, b) -> {
- if (a.detail != b.detail) return a.detail - b.detail;
+ if (a.detailLevel != b.detailLevel) return a.detailLevel - b.detailLevel;
int aDist = a.getCenter().toPos2D().chebyshevDist(Pos2D.ZERO);
int bDist = b.getCenter().toPos2D().chebyshevDist(Pos2D.ZERO);
if (aDist != bDist) return aDist - bDist;
@@ -154,13 +153,13 @@ public class GenerationQueue implements Closeable {
}
if (requiredDataDetail > maxDataDetail) requiredDataDetail = maxDataDetail;
- LodUtil.assertTrue(pos.detail > requiredDataDetail+4);
- byte granularity = (byte) (pos.detail - requiredDataDetail);
+ LodUtil.assertTrue(pos.detailLevel > requiredDataDetail+4);
+ byte granularity = (byte) (pos.detailLevel - requiredDataDetail);
if (granularity > maxGranularity) {
// Too big of a chunk. We need to split it up
byte subDetail = (byte) (maxGranularity + requiredDataDetail);
- int subPosCount = pos.getWidth(subDetail);
+ int subPosCount = pos.getBlockWidth(subDetail);
DhLodPos cornerSubPos = pos.getCorner(subDetail);
CompletableFuture[] subFutures = new CompletableFuture[subPosCount*subPosCount];
ArrayList subTasks = new ArrayList<>(subPosCount*subPosCount);
@@ -171,7 +170,7 @@ public class GenerationQueue implements Closeable {
for (int oz = 0; oz < subPosCount; oz++) {
CompletableFuture subFuture = new CompletableFuture<>();
subFutures[i++] = subFuture;
- subTasks.add(new GenTask(cornerSubPos.offset(ox, oz), requiredDataDetail, splitTask, subFuture));
+ subTasks.add(new GenTask(cornerSubPos.addOffset(ox, oz), requiredDataDetail, splitTask, subFuture));
}
}
}
@@ -207,7 +206,7 @@ public class GenerationQueue implements Closeable {
}
private void addAndCombineGroup(TaskGroup target) {
- byte granularity = (byte) (target.pos.detail - target.dataDetail);
+ byte granularity = (byte) (target.pos.detailLevel - target.dataDetail);
LodUtil.assertTrue(granularity <= maxGranularity && granularity >= minGranularity);
LodUtil.assertTrue(!taskGroups.containsKey(target.pos));
@@ -221,7 +220,7 @@ public class GenerationQueue implements Closeable {
if (!group.pos.overlaps(target.pos)) continue;
// We should have already ALWAYS selected the higher granularity.
- LodUtil.assertTrue(group.pos.detail < target.pos.detail);
+ LodUtil.assertTrue(group.pos.detailLevel < target.pos.detailLevel);
groupIter.remove(); // Remove and consume all from that lower granularity request
target.members.addAll(group.members);
}
@@ -231,12 +230,12 @@ public class GenerationQueue implements Closeable {
if (granularity < maxGranularity) { // Obviously, only do so if we aren't at the maxGranularity already
// Check for merging and upping the granularity
DhLodPos corePos = target.pos;
- DhLodPos parentPos = corePos.convertUpwardsTo((byte) (corePos.detail+1));
+ DhLodPos parentPos = corePos.convertUpwardsTo((byte) (corePos.detailLevel +1));
int targetChildId = target.pos.getChildIndexOfParent();
boolean allPassed = true;
for (int i = 0; i < 4; i++) {
if (i == targetChildId) continue;
- TaskGroup group = taskGroups.get(parentPos.getChild(i));
+ TaskGroup group = taskGroups.get(parentPos.getChildByIndex(i));
if (group == null || group.dataDetail != target.dataDetail) {
allPassed = false;
break;
@@ -247,7 +246,7 @@ public class GenerationQueue implements Closeable {
TaskGroup[] groups = new TaskGroup[4];
for (int i = 0; i < 4; i++) {
if (i==targetChildId) groups[i] = target;
- else groups[i] = taskGroups.remove(parentPos.getChild(i));
+ else groups[i] = taskGroups.remove(parentPos.getChildByIndex(i));
LodUtil.assertTrue(groups[i] != null && groups[i].dataDetail == target.dataDetail);
}
@@ -286,7 +285,7 @@ public class GenerationQueue implements Closeable {
GenTask task = looseTasks.poll();
taskProcessed++;
byte taskDataDetail = task.dataDetail;
- byte taskGranularity = (byte) (task.pos.detail - taskDataDetail);
+ byte taskGranularity = (byte) (task.pos.detailLevel - taskDataDetail);
LodUtil.assertTrue(taskGranularity >= 4 && taskGranularity >= minGranularity && taskGranularity <= maxGranularity);
// Check existing one
@@ -401,7 +400,7 @@ public class GenerationQueue implements Closeable {
private void startTaskGroup(InProgressTask task) {
byte dataDetail = task.group.dataDetail;
DhLodPos pos = task.group.pos;
- byte granularity = (byte) (pos.detail - dataDetail);
+ byte granularity = (byte) (pos.detailLevel - dataDetail);
LodUtil.assertTrue(granularity >= minGranularity && granularity <= maxGranularity);
LodUtil.assertTrue(dataDetail >= minDataDetail && dataDetail <= maxDataDetail);
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 b293edaf5..039313450 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
@@ -141,9 +141,9 @@ public class DhClientServerLevel implements IDhClientLevel, IDhServerLevel
RenderState rs = renderState.get();
DhLodPos pos = data.getBBoxLodPos().convertUpwardsTo(FullDataSource.SECTION_SIZE_OFFSET);
if (rs != null) {
- rs.renderFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data);
+ rs.renderFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
} else {
- dataFileHandler.write(new DhSectionPos(pos.detail, pos.x, pos.z), data);
+ dataFileHandler.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data);
}
}
diff --git a/core/src/main/java/com/seibel/lod/core/pos/DhLodPos.java b/core/src/main/java/com/seibel/lod/core/pos/DhLodPos.java
index 3591c2e1e..966e93988 100644
--- a/core/src/main/java/com/seibel/lod/core/pos/DhLodPos.java
+++ b/core/src/main/java/com/seibel/lod/core/pos/DhLodPos.java
@@ -1,105 +1,165 @@
package com.seibel.lod.core.pos;
+import com.seibel.lod.core.util.BitShiftUtil;
import com.seibel.lod.core.util.LodUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
-public class DhLodPos implements Comparable {
- public final byte detail;
- public final int x;
- public final int z;
-
- public DhLodPos(byte detail, int x, int z) {
- this.detail = detail;
- this.x = x;
- this.z = z;
- }
-
- public String toString() {
- return "[" + detail + "*" + x + "," + z + "]";
- }
-
- public DhLodUnit getX() {
- return new DhLodUnit(detail, x);
- }
-
- public DhLodUnit getZ() {
- return new DhLodUnit(detail, z);
- }
-
- public int getWidth() {
- return 1 << detail;
- }
- public int getWidth(byte detail) {
- LodUtil.assertTrue(detail <= this.detail);
- return 1 << (this.detail - detail);
- }
-
- public static int blockWidth(byte detail) {
- return 1 << detail;
- }
-
- public DhBlockPos2D getCenter() {
- return new DhBlockPos2D(getX().toBlock() + (getWidth() >> 1), getZ().toBlock() + (getWidth() >> 1));
- }
- public DhBlockPos2D getCorner() {
- return new DhBlockPos2D(getX().toBlock(), getZ().toBlock());
- }
-
- public DhLodPos getCorner(byte newDetail) {
- LodUtil.assertTrue(newDetail <= detail);
- return new DhLodPos(newDetail, x << (detail-newDetail), z << (detail-newDetail));
- }
-
- public DhLodPos convertUpwardsTo(byte newDetail) {
- LodUtil.assertTrue(newDetail >= detail);
- return new DhLodPos(newDetail, Math.floorDiv(x, 1<<(newDetail-detail)), Math.floorDiv(z, 1<<(newDetail-detail)));
- }
- public DhLodPos getChild(int child0to3) {
- if (child0to3 < 0 || child0to3 > 3) throw new IllegalArgumentException("child0to3 must be between 0 and 3");
- if (detail <= 0) throw new IllegalStateException("detail must be greater than 0");
- return new DhLodPos((byte) (detail - 1),
- x * 2 + (child0to3 & 1),
- z * 2 + ((child0to3 & 2) >> 1));
- }
- public int getChildIndexOfParent() {
- return (x & 1) + ((z & 1) << 1);
- }
-
+/**
+ * A MC world position that is relative to a given detail level.
+ *
+ * @author Leetom
+ * @version 2022-11-6
+ */
+public class DhLodPos implements Comparable
+{
+ public final byte detailLevel;
+ public final int x;
+ public final int z;
+
+
+
+ public DhLodPos(byte detailLevel, int x, int z)
+ {
+ this.detailLevel = detailLevel;
+ this.x = x;
+ this.z = z;
+ }
+
+
+
+ public DhLodUnit getX() { return new DhLodUnit(this.detailLevel, this.x); }
+ public DhLodUnit getZ() { return new DhLodUnit(this.detailLevel, this.z); }
+
+ public int getBlockWidth() { return this.getBlockWidth(this.detailLevel); }
+ public int getBlockWidth(byte detailLevel)
+ {
+ LodUtil.assertTrue(detailLevel <= this.detailLevel);
+ return BitShiftUtil.powerOfTwo(this.detailLevel - detailLevel);
+ }
+
+ public DhBlockPos2D getCenter()
+ {
+ return new DhBlockPos2D(
+ this.getX().toBlockWidth() + BitShiftUtil.half(this.getBlockWidth()),
+ this.getZ().toBlockWidth() + BitShiftUtil.half(this.getBlockWidth()));
+ }
+ public DhBlockPos2D getCorner() { return new DhBlockPos2D(this.getX().toBlockWidth(), this.getZ().toBlockWidth()); }
+
+ public DhLodPos getCorner(byte newDetail)
+ {
+ LodUtil.assertTrue(newDetail <= this.detailLevel);
+ return new DhLodPos(newDetail,
+ this.x * BitShiftUtil.powerOfTwo(this.detailLevel - newDetail),
+ this.z * BitShiftUtil.powerOfTwo(this.detailLevel - newDetail));
+ }
+
+ public DhLodPos convertUpwardsTo(byte newDetail)
+ {
+ LodUtil.assertTrue(newDetail >= this.detailLevel);
+ return new DhLodPos(newDetail,
+ Math.floorDiv(this.x, BitShiftUtil.powerOfTwo(newDetail - this.detailLevel)),
+ Math.floorDiv(this.z, BitShiftUtil.powerOfTwo(newDetail - this.detailLevel)));
+ }
+
+ /**
+ * Returns the DhLodPos 1 detail level lower
+ *
+ * Relative child positions returned for each index:
+ * 0 = (0,0)
+ * 1 = (1,0)
+ * 2 = (0,1)
+ * 3 = (1,1)
+ *
+ * @param child0to3 must be an int between 0 and 3
+ */
+ public DhLodPos getChildByIndex(int child0to3) throws IllegalArgumentException, IllegalStateException
+ {
+ if (child0to3 < 0 || child0to3 > 3)
+ throw new IllegalArgumentException("child0to3 must be between 0 and 3");
+ if (this.detailLevel <= 0)
+ throw new IllegalStateException("detailLevel must be greater than 0");
+
+ return new DhLodPos((byte) (this.detailLevel - 1),
+ this.x * 2 + (child0to3 & 1),
+ this.z * 2 + BitShiftUtil.half(child0to3 & 2));
+ }
+ /** Returns this position's child index in its parent */
+ public int getChildIndexOfParent() { return (this.x & 1) + ((this.z & 1) << 1); }
+
+ public boolean overlaps(DhLodPos other)
+ {
+ if (this.equals(other))
+ return true;
+ if (this.detailLevel == other.detailLevel)
+ return false;
+
+ if (this.detailLevel > other.detailLevel)
+ {
+ return this.equals(other.convertUpwardsTo(this.detailLevel));
+ }
+ else
+ {
+ return other.equals(this.convertUpwardsTo(other.detailLevel));
+ }
+ }
+
+ /** Only valid for DhLodUnits for an equal or greater detail level */
+ public DhLodPos addLodUnit(DhLodUnit width)
+ {
+ if (width.detailLevel < this.detailLevel)
+ throw new IllegalArgumentException("add called with width.detailLevel < pos detail");
+
+ return new DhLodPos(this.detailLevel,
+ x + width.createFromDetailLevel(this.detailLevel).numberOfLodSectionsWide,
+ z + width.createFromDetailLevel(this.detailLevel).numberOfLodSectionsWide);
+ }
+
+ /** Equivalent to adding a DhLodUnit with the same detail level as this DhLodPos */
+ public DhLodPos addOffset(int xOffset, int zOffset) { return new DhLodPos(this.detailLevel, this.x + xOffset, this.z + zOffset); }
+
+
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj == null || this.getClass() != obj.getClass())
+ {
+ return false;
+ }
+ else
+ {
+ DhLodPos otherPos = (DhLodPos) obj;
+ return this.detailLevel == otherPos.detailLevel && this.x == otherPos.x && this.z == otherPos.z;
+ }
+ }
+
@Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DhLodPos dhLodPos = (DhLodPos) o;
- return detail == dhLodPos.detail && x == dhLodPos.x && z == dhLodPos.z;
- }
-
+ public int hashCode() { return Objects.hash(detailLevel, x, z); }
+
@Override
- public int hashCode() {
- return Objects.hash(detail, x, z);
- }
-
- public boolean overlaps(DhLodPos other) {
- if (equals(other)) return true;
- if (detail == other.detail) return false;
- if (detail > other.detail) {
- return this.equals(other.convertUpwardsTo(this.detail));
- } else {
- return other.equals(this.convertUpwardsTo(other.detail));
- }
- }
-
- public DhLodPos add(DhLodUnit width) {
- if (width.detail < detail) throw new IllegalArgumentException("add called with width.detail < pos detail");
- return new DhLodPos(detail, x + width.convertTo(detail).value, z + width.convertTo(detail).value);
- }
- public DhLodPos offset(int ox, int oz) {
- return new DhLodPos(detail, x+ox, z+oz);
- }
-
- @Override
- public int compareTo(@NotNull DhLodPos o) {
- return detail != o.detail ? Integer.compare(detail, o.detail) : x != o.x ? Integer.compare(x, o.x) : Integer.compare(z, o.z);
- }
+ public int compareTo(@NotNull DhLodPos obj)
+ {
+ if (this.detailLevel != obj.detailLevel)
+ {
+ return Integer.compare(this.detailLevel, obj.detailLevel);
+ }
+ else if (this.x != obj.x)
+ {
+ return Integer.compare(this.x, obj.x);
+ }
+ else
+ {
+ return Integer.compare(this.z, obj.z);
+ }
+ }
+
+ @Override
+ public String toString() { return "[" + this.detailLevel + "*" + this.x + "," + this.z + "]"; }
+
}
diff --git a/core/src/main/java/com/seibel/lod/core/pos/DhLodUnit.java b/core/src/main/java/com/seibel/lod/core/pos/DhLodUnit.java
index 2b6fe2099..b7dd65cd8 100644
--- a/core/src/main/java/com/seibel/lod/core/pos/DhLodUnit.java
+++ b/core/src/main/java/com/seibel/lod/core/pos/DhLodUnit.java
@@ -1,29 +1,54 @@
package com.seibel.lod.core.pos;
-public class DhLodUnit {
- public final byte detail;
- public final int value;
+import com.seibel.lod.core.util.BitShiftUtil;
- public DhLodUnit(byte detail, int value) {
- this.detail = detail;
- this.value = value;
- }
-
- public int toBlock() {
- return value << detail;
- }
-
- public static DhLodUnit fromBlock(int block, byte targetDetail) {
- return new DhLodUnit(targetDetail, Math.floorDiv(block, 1< targetDetail) { //TODO check if this is correct
- return new DhLodUnit(targetDetail, value << (detail - targetDetail));
- }
- return new DhLodUnit(targetDetail, Math.floorDiv(value, 1<<(targetDetail-detail)));
+/**
+ * Often used to measure LOD widths
+ *
+ * @author Leetom
+ * @version 2022-11-6
+ */
+public class DhLodUnit
+{
+ /** The detail level of this LOD Unit */
+ public final byte detailLevel;
+ /** How many LOD columns wide this LOD Unit represents */
+ public final int numberOfLodSectionsWide;
+
+
+
+ public DhLodUnit(byte detailLevel, int numberOfLodSectionsWide)
+ {
+ this.detailLevel = detailLevel;
+ this.numberOfLodSectionsWide = numberOfLodSectionsWide;
}
+
+
+ /** @return the size of this LOD unit in Minecraft blocks */
+ public int toBlockWidth() { return this.numberOfLodSectionsWide << this.detailLevel; }
+ /** @return the LOD Unit relative to the given block width and detail level */
+ public static DhLodUnit fromBlockWidth(int blockWidth, byte targetDetailLevel) { return new DhLodUnit(targetDetailLevel, Math.floorDiv(blockWidth, BitShiftUtil.powerOfTwo(targetDetailLevel))); }
+
+ /**
+ * if the targetDetailLevel and this object's detail are the same,
+ * this will be returned instead of creating a new object
+ */
+ public DhLodUnit createFromDetailLevel(byte targetDetailLevel)
+ {
+ if (this.detailLevel == targetDetailLevel)
+ {
+ // no need to create a new object, this one is already the right detail level
+ return this;
+ }
+ else if (this.detailLevel > targetDetailLevel)
+ {
+ //TODO check if this is correct
+ return new DhLodUnit(targetDetailLevel, this.numberOfLodSectionsWide * BitShiftUtil.powerOfTwo(this.detailLevel - targetDetailLevel));
+ }
+ else
+ {
+ return new DhLodUnit(targetDetailLevel, Math.floorDiv(this.numberOfLodSectionsWide, BitShiftUtil.powerOfTwo(targetDetailLevel - this.detailLevel)));
+ }
+ }
+
}
diff --git a/core/src/main/java/com/seibel/lod/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/lod/core/pos/DhSectionPos.java
index 26fb6fbc9..6bf0b412f 100644
--- a/core/src/main/java/com/seibel/lod/core/pos/DhSectionPos.java
+++ b/core/src/main/java/com/seibel/lod/core/pos/DhSectionPos.java
@@ -1,112 +1,150 @@
package com.seibel.lod.core.pos;
import com.seibel.lod.core.enums.ELodDirection;
+import com.seibel.lod.core.util.BitShiftUtil;
import com.seibel.lod.core.util.LodUtil;
+import com.seibel.lod.core.util.MathUtil;
import java.util.function.Consumer;
-public class DhSectionPos {
- public final byte sectionDetail;
- public final int sectionX; // in sectionDetail level grid
- public final int sectionZ; // in sectionDetail level grid
-
- public DhSectionPos(byte sectionDetail, int sectionX, int sectionZ) {
- this.sectionDetail = sectionDetail;
- this.sectionX = sectionX;
- this.sectionZ = sectionZ;
- }
-
- public DhLodPos getCenter(byte returnDetailLevel) {
- LodUtil.assertTrue(returnDetailLevel <= sectionDetail, "returnDetailLevel must be less than sectionDetail");
- if (returnDetailLevel == sectionDetail)
- return new DhLodPos(sectionDetail, sectionX, sectionZ);
- byte offset = (byte) (sectionDetail - returnDetailLevel);
- return new DhLodPos(returnDetailLevel, (sectionX << offset)+(1 << (offset -1)),
- (sectionZ << offset)+(1 << (offset -1)));
- }
- public DhLodPos getCorner(byte returnDetailLevel) {
- LodUtil.assertTrue(returnDetailLevel <= sectionDetail, "returnDetailLevel must be less than sectionDetail");
- byte offset = (byte) (sectionDetail - returnDetailLevel);
- return new DhLodPos(returnDetailLevel, sectionX << offset, sectionZ << offset);
- }
- public DhLodUnit getWidth(byte returnDetailLevel) {
- LodUtil.assertTrue(returnDetailLevel <= sectionDetail, "returnDetailLevel must be less than sectionDetail");
- byte offset = (byte) (sectionDetail - returnDetailLevel);
- return new DhLodUnit(sectionDetail, 1 << offset);
- }
- public DhLodPos getCenter() {
- return getCenter((byte)0);
- }
- public DhLodPos getCorner() {
- return getCorner((byte) (sectionDetail-1));
- }
- public DhLodUnit getWidth() {
- return getWidth(sectionDetail);
- }
-
- public DhSectionPos getChild(int child0to3){
- if (child0to3 < 0 || child0to3 > 3) throw new IllegalArgumentException("child0to3 must be between 0 and 3");
- if (sectionDetail <= 0) throw new IllegalStateException("section detail must be greater than 0");
- return new DhSectionPos((byte) (sectionDetail - 1),
- sectionX * 2 + (child0to3 & 1),
- sectionZ * 2 + ((child0to3 & 2) >> 1));
- }
- public int getChildIndexOfParent() {
- return (sectionX & 1) + ((sectionZ & 1) << 1);
- }
-
- public void forEachChild(Consumer callback){
- for (int i = 0; i < 4; i++) {
- callback.accept(getChild(i));
- }
- }
-
- public DhSectionPos getParent(){
- return new DhSectionPos((byte) (sectionDetail + 1), sectionX >> 1, sectionZ >> 1);
- }
-
- public DhSectionPos getAdjacent(ELodDirection dir) {
- return new DhSectionPos(sectionDetail, sectionX + dir.getNormal().x, sectionZ + dir.getNormal().z);
- }
-
- public DhLodPos getSectionBBoxPos() {
- return new DhLodPos(sectionDetail, sectionX, sectionZ);
- }
-
- /**
- * NOTE: This does not consider yOffset!
- */
- public boolean overlaps(DhSectionPos other){
- return getSectionBBoxPos().overlaps(other.getSectionBBoxPos());
- }
-
- @Override
- public String toString() {
- return "{" + sectionDetail +
- "*" + sectionX +
- "," + sectionZ +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DhSectionPos that = (DhSectionPos) o;
- return sectionDetail == that.sectionDetail &&
- sectionX == that.sectionX &&
- sectionZ == that.sectionZ;
- }
-
- @Override
- public int hashCode() {
- return Integer.hashCode(sectionDetail) ^
- Integer.hashCode(sectionX) ^
- Integer.hashCode(sectionZ);
- }
-
- // Serialize() is different from toString() as this requires it to NEVER be changed, and should be in a short format
- public String serialize() {
- return "[" + sectionDetail + ',' + sectionX + ',' + sectionZ + ']';
- }
+/**
+ * The position object used to define LOD objects in the quad trees.
+ *
+ * @author Leetom
+ * @version 2022-11-6
+ */
+public class DhSectionPos
+{
+ public final byte sectionDetail;
+
+ /** in sectionDetail level grid */
+ public final int sectionX;
+ /** in sectionDetail level grid */
+ public final int sectionZ;
+
+
+
+ public DhSectionPos(byte sectionDetail, int sectionX, int sectionZ)
+ {
+ this.sectionDetail = sectionDetail;
+ this.sectionX = sectionX;
+ this.sectionZ = sectionZ;
+ }
+
+
+
+ /** Returns the center for the highest detail level (0) */
+ public DhLodPos getCenter() { return this.getCenter((byte) 0); }
+ public DhLodPos getCenter(byte returnDetailLevel)
+ {
+ LodUtil.assertTrue(returnDetailLevel <= this.sectionDetail, "returnDetailLevel must be less than sectionDetail");
+
+ if (returnDetailLevel == this.sectionDetail)
+ return new DhLodPos(this.sectionDetail, this.sectionX, this.sectionZ);
+
+ byte offset = (byte) (this.sectionDetail - returnDetailLevel);
+ return new DhLodPos(returnDetailLevel,
+ (this.sectionX * BitShiftUtil.powerOfTwo(offset)) + BitShiftUtil.powerOfTwo(offset - 1),
+ (this.sectionZ * BitShiftUtil.powerOfTwo(offset)) + BitShiftUtil.powerOfTwo(offset - 1));
+ }
+
+ /** @return the corner with the smallest X and Z coordinate */
+ public DhLodPos getCorner() { return this.getCorner((byte) (this.sectionDetail - 1)); }
+ /** @return the corner with the smallest X and Z coordinate */
+ public DhLodPos getCorner(byte returnDetailLevel)
+ {
+ LodUtil.assertTrue(returnDetailLevel <= this.sectionDetail, "returnDetailLevel must be less than sectionDetail");
+ byte offset = (byte) (this.sectionDetail - returnDetailLevel);
+ return new DhLodPos(returnDetailLevel,
+ this.sectionX * BitShiftUtil.powerOfTwo(offset),
+ this.sectionZ * BitShiftUtil.powerOfTwo(offset));
+ }
+
+ public DhLodUnit getWidth() { return this.getWidth(this.sectionDetail); }
+ public DhLodUnit getWidth(byte returnDetailLevel)
+ {
+ LodUtil.assertTrue(returnDetailLevel <= this.sectionDetail, "returnDetailLevel must be less than sectionDetail");
+ byte offset = (byte) (this.sectionDetail - returnDetailLevel);
+ return new DhLodUnit(this.sectionDetail, BitShiftUtil.powerOfTwo(offset));
+ }
+
+
+ /**
+ * Returns the DhLodPos 1 detail level lower
+ *
+ * Relative child positions returned for each index:
+ * 0 = (0,0)
+ * 1 = (1,0)
+ * 2 = (0,1)
+ * 3 = (1,1)
+ *
+ * @param child0to3 must be an int between 0 and 3
+ */
+ public DhSectionPos getChildByIndex(int child0to3) throws IllegalArgumentException, IllegalStateException
+ {
+ if (child0to3 < 0 || child0to3 > 3)
+ throw new IllegalArgumentException("child0to3 must be between 0 and 3");
+ if (this.sectionDetail <= 0)
+ throw new IllegalStateException("section detail must be greater than 0");
+
+ return new DhSectionPos((byte) (this.sectionDetail - 1),
+ this.sectionX * 2 + (child0to3 & 1),
+ this.sectionZ * 2 + BitShiftUtil.half(child0to3 & 2));
+ }
+ /** Returns this position's child index in its parent */
+ public int getChildIndexOfParent() { return (this.sectionX & 1) + ((this.sectionZ & 1) << 1); }
+
+ /** Applies the given consumer to all 4 of this position's children. */
+ public void forEachChild(Consumer callback)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ callback.accept(this.getChildByIndex(i));
+ }
+ }
+
+ public DhSectionPos getParentPos() { return new DhSectionPos((byte) (this.sectionDetail + 1), BitShiftUtil.half(this.sectionX), BitShiftUtil.half(this.sectionZ)); }
+
+ public DhSectionPos getAdjacentPos(ELodDirection dir)
+ {
+ return new DhSectionPos(this.sectionDetail,
+ this.sectionX + dir.getNormal().x,
+ this.sectionZ + dir.getNormal().z);
+ }
+
+ public DhLodPos getSectionBBoxPos() { return new DhLodPos(this.sectionDetail, this.sectionX, this.sectionZ); }
+
+ /** NOTE: This does not consider yOffset! */
+ public boolean overlaps(DhSectionPos other) { return this.getSectionBBoxPos().overlaps(other.getSectionBBoxPos()); }
+
+ /** Serialize() is different from toString() as it must NEVER be changed, and should be in a short format */
+ public String serialize() { return "[" + this.sectionDetail + ',' + this.sectionX + ',' + this.sectionZ + ']'; }
+
+
+
+ @Override
+ public String toString() { return "{" + this.sectionDetail + "*" + this.sectionX + "," + this.sectionZ + "}"; }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null || this.getClass() != obj.getClass())
+ return false;
+
+ DhSectionPos that = (DhSectionPos) obj;
+ return this.sectionDetail == that.sectionDetail &&
+ this.sectionX == that.sectionX &&
+ this.sectionZ == that.sectionZ;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Integer.hashCode(this.sectionDetail) ^ // XOR
+ Integer.hashCode(this.sectionX) ^ // XOR
+ Integer.hashCode(this.sectionZ);
+ }
+
}
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 ea8402dc3..7684b7d36 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
@@ -181,7 +181,7 @@ public class LodQuadTree implements AutoCloseable {
* @return the parent LodSection
*/
public LodRenderSection getParentSection(DhSectionPos pos) {
- return getSection(pos.getParent());
+ return getSection(pos.getParentPos());
}
/**
@@ -192,7 +192,7 @@ public class LodQuadTree implements AutoCloseable {
* @return one of the child LodSection
*/
public LodRenderSection getChildSection(DhSectionPos pos, int child0to3) {
- return getSection(pos.getChild(child0to3));
+ return getSection(pos.getChildByIndex(child0to3));
}
private LodRenderSection _set(MovableGridRingList list, int x, int z, LodRenderSection t) {
@@ -280,15 +280,15 @@ public class LodQuadTree implements AutoCloseable {
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.getParent());
- parent = _set(parentRingList, pos.x >> 1, pos.y >> 1, new LodRenderSection(section.pos.getParent()));
+ 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.getParent(), 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.getChild(i);
+ DhSectionPos childPos = section.pos.getChildByIndex(i);
LodUtil.assertTrue(childRingList != null);
LodRenderSection child = _get(childRingList, childPos.sectionX, childPos.sectionZ);
if (child == null) {
@@ -338,11 +338,11 @@ public class LodQuadTree implements AutoCloseable {
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.getParent());
- parent = _set(parentRingList, pos.x >> 1, pos.y >> 1, new LodRenderSection(sectPos.getParent()));
+ 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.getParent(), parent.childCount);
+ if (SUPER_VERBOSE_LOGGING) LOGGER.info("parent sect {} now has {} childs.", sectPos.getParentPos(), parent.childCount);
}
}
}
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 b4e9f9b13..578d906eb 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
@@ -156,7 +156,7 @@ public class RenderBufferHandler {
if (children == null) {
RenderBufferNode[] childs = new RenderBufferNode[4];
for (int i = 0; i < 4; i++) {
- childs[i] = new RenderBufferNode(pos.getChild(i));
+ childs[i] = new RenderBufferNode(pos.getChildByIndex(i));
}
children = childs;
}