diff --git a/src/main/java/com/seibel/lod/core/objects/Pos2D.java b/src/main/java/com/seibel/lod/core/objects/Pos2D.java index 53ba0c8fe..13a9f224c 100644 --- a/src/main/java/com/seibel/lod/core/objects/Pos2D.java +++ b/src/main/java/com/seibel/lod/core/objects/Pos2D.java @@ -19,11 +19,26 @@ package com.seibel.lod.core.objects; -public final class Pos2D { +import com.seibel.lod.core.util.LodUtil; + +public class Pos2D { public final int x; public final int y; public Pos2D(int x, int y) { this.x = x; this.y = y; } + + public Pos2D add(Pos2D other) { + return new Pos2D(x + other.x, y + other.y); + } + public Pos2D subtract(Pos2D other) { + return new Pos2D(x - other.x, y - other.y); + } + public double dist(Pos2D other) { + return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(y - other.y, 2)); + } + public long distSquared(Pos2D other) { + return LodUtil.pow2((long)x - other.x) + LodUtil.pow2((long)y - other.y); + } } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java b/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java index fd0fa1311..efd225a26 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java @@ -1,6 +1,7 @@ package com.seibel.lod.core.objects.a7; import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; +import com.seibel.lod.core.objects.a7.data.DataHandler; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -10,6 +11,7 @@ public class DHLevel { private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class); public final File saveFolder; // Could be null, for no saving + public final DataHandler dataHandler; // Could be null, for no saving public LodQuadTree lodQuadTree; public DHLevel(File saveFolder) { this.saveFolder = saveFolder; @@ -18,5 +20,12 @@ public class DHLevel { MC.getPlayerBlockPos().x, MC.getPlayerBlockPos().z ); + if (saveFolder != null) { + dataHandler = new DataHandler(saveFolder); + } else { + dataHandler = null; + } } + + } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/IdMappingUtil.java b/src/main/java/com/seibel/lod/core/objects/a7/IdMappingUtil.java new file mode 100644 index 000000000..857b58f69 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/IdMappingUtil.java @@ -0,0 +1,6 @@ +package com.seibel.lod.core.objects.a7; + +public class IdMappingUtil { + public static final String BLOCKSTATE_ID_AIR = "air"; + //TODO HERE +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/LodDataSource.java b/src/main/java/com/seibel/lod/core/objects/a7/LodDataSource.java deleted file mode 100644 index 099f46667..000000000 --- a/src/main/java/com/seibel/lod/core/objects/a7/LodDataSource.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.seibel.lod.core.objects.a7; - -public interface LodDataSource { - RenderDataContaioner createRenderData(byte detailLevel, int x, int z); - - - boolean saveLodData(RenderDataContaioner levelContainer, byte detailLevel, int x, int z); -} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java b/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java index 8beac3f03..025d62c88 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java @@ -1,26 +1,124 @@ package com.seibel.lod.core.objects.a7; +import com.seibel.lod.core.objects.Pos2D; +import com.seibel.lod.core.objects.a7.pos.DhBlockPos2D; +import com.seibel.lod.core.objects.a7.pos.DhLodUnit; +import com.seibel.lod.core.objects.a7.pos.DhSectionPos; import com.seibel.lod.core.util.DetailDistanceUtil; +import com.seibel.lod.core.util.LodUtil; import com.seibel.lod.core.util.gridList.MovableGridRingList; // QuadTree built from several layers of 2d ring buffers public class LodQuadTree { public final int maxPossibleDetailLevel; - private final MovableGridRingList[] ringLists; + private final MovableGridRingList[] ringLists; public LodQuadTree(int viewDistance, int initialPlayerX, int initialPlayerZ) { maxPossibleDetailLevel = DetailDistanceUtil.getDetailLevelFromDistance(viewDistance*Math.sqrt(2)); ringLists = new MovableGridRingList[maxPossibleDetailLevel]; int size; - for (int detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { + for (byte detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { double distance = DetailDistanceUtil.getDrawDistanceFromDetail(detailLevel); - int blockCount = ((int)Math.ceil(distance / (1 << detailLevel))); - ringLists[detailLevel] = new MovableGridRingList(blockCount, initialPlayerX >> detailLevel, initialPlayerZ >> detailLevel); - size = ringLists[detailLevel].getSize(); + int sectionCount = LodUtil.ceilDiv((int) Math.ceil(distance), + DhSectionPos.getWidth(detailLevel).toBlock()) + 1; // +1 for the border during move + ringLists[detailLevel] = new MovableGridRingList(sectionCount, + initialPlayerX >> detailLevel, initialPlayerZ >> detailLevel); } } + public LodSection getSection(DhSectionPos pos) { + return getSection(pos.detail, pos.x, pos.z); + } + + public LodSection getSection(byte detailLevel, int x, int z) { + return ringLists[detailLevel].get(x, z); + } + + enum LodSectionState { + Loaded, + Unloaded, + Freed, + } + + /* + private LodSectionState expectsState(DhBlockPos2D playerPos, DhSectionPos pos) { + // Get state of the children + boolean hasAnyChildren = false; + if (pos.detail != 0) { + hasAnyChildren = getSection(pos.getChild(0)) != null || + getSection(pos.getChild(1)) != null || + getSection(pos.getChild(2)) != null || + getSection(pos.getChild(3)) != null; // Do this to allow short-circuit + } + if (hasAnyChildren) { + return LodSectionState.Unloaded; + } + // All children is in the Freed state + + // Calculate the distance to the player + long dist = pos.getCenter().getCenter().distSquared(playerPos); + byte targetDetail = DetailDistanceUtil.getDetailLevelFromDistance(dist); + }*/ + + public void tick(DhBlockPos2D playerPos) { + for (int detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { + ringLists[detailLevel].move(playerPos.x >> detailLevel, playerPos.z >> detailLevel); + } + + // First tick pass: update all sections' distanceBasedTargetLevel amd neighborCheckedTargetLevel + for (byte detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { + final MovableGridRingList ringList = ringLists[detailLevel]; + final MovableGridRingList childRingList = detailLevel == 0 ? null : ringLists[detailLevel - 1]; + final byte detail = detailLevel; + ringList.forEachPosOrdered((r, pos) -> { + DhSectionPos sectionPos = new DhSectionPos(detail, pos.x, pos.y); + long dist = sectionPos.getCenter().getCenter().distSquared(playerPos); + byte targetDetail = DetailDistanceUtil.getDetailLevelFromDistance(dist); + if (r == null) { + if (targetDetail <= detail) { + r = ringList.setChained(pos.x, pos.y, new LodSection(sectionPos)); + } else { + return; + } + } + r.distanceBasedTargetLevel = targetDetail; + if (childRingList == null) { + r.childTargetLevel = (byte) (r.distanceBasedTargetLevel - 1); + } else { + byte minChildLevel = Byte.MAX_VALUE; + /* FIXME: Todo later + DhSectionPos childPos0 = sectionPos.getChild(0); + minChildLevel = LodUtil.min(minChildLevel, childRingList.get(childPos0.x, childPos0.z).childTargetLevel); + DhSectionPos childPos1 = sectionPos.getChild(1); + minChildLevel = LodUtil.min(minChildLevel, childRingList.get(childPos1.x, childPos1.z).childTargetLevel); + DhSectionPos childPos2 = sectionPos.getChild(2); + minChildLevel = LodUtil.min(minChildLevel, childRingList.get(childPos2.x, childPos2.z).childTargetLevel); + DhSectionPos childPos3 = sectionPos.getChild(3); + minChildLevel = LodUtil.min(minChildLevel, childRingList.get(childPos3.x, childPos3.z).childTargetLevel); + r.childTargetLevel = minChildLevel + 1;*/ + } + }); + } + + // Second tick pass: load, unload, and free sections + for (byte detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { + final MovableGridRingList ringList = ringLists[detailLevel]; + final byte detail = detailLevel; + + } + + // Update the tree from the bottom detail level upwards + for (byte detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { + final MovableGridRingList ringList = ringLists[detailLevel]; + final byte detail = detailLevel; + ringList.forEachPosOrdered((r, pos) -> { + DhSectionPos sectionPos = new DhSectionPos(detail, pos.x, pos.y); + }); + } + + } + } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java b/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java index 3437d8622..f1aa40e40 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java @@ -1,38 +1,42 @@ package com.seibel.lod.core.objects.a7; -import com.seibel.lod.core.objects.lod.VerticalLevelContainer; +import com.seibel.lod.core.objects.a7.pos.DhSectionPos; public class LodSection { public static final int SUB_REGION_DATA_WIDTH = 16*16; - public final byte detailLevel; - public final int x; - public final int z; - private RenderDataContaioner levelContainer; + public final DhSectionPos pos; + + // Following used for LodQuadTree tick() method, and ONLY for that method! + public byte distanceBasedTargetLevel = Byte.MAX_VALUE; // the pure distance-based target level of this section + // what is the nearest target level for the child quadrants after making sure child quadrants use the same target level? + public byte childTargetLevel = Byte.MAX_VALUE; + + private RenderDataContainer levelContainer; private RenderContainer renderContainer = null; // Create sub region - public LodSection(byte detailLevel, int x, int z) { - this.detailLevel = detailLevel; - this.x = x; - this.z = z; + public LodSection(DhSectionPos pos) { + this.pos = pos; levelContainer = null; } - LodSection(byte detailLevel, int x, int z, RenderDataContaioner levelContainer) { - this.detailLevel = detailLevel; - this.x = x; - this.z = z; + LodSection(DhSectionPos pos, RenderDataContainer levelContainer) { + this.pos = pos; this.levelContainer = levelContainer; } // Return null if data does not exist - public static LodSection loadSection(byte detailLevel, int x, int z, LodDataSource lodDataSource) { - RenderDataContaioner data = lodDataSource.createRenderData(detailLevel, x, z); - if (data == null) { - return null; - } - return new LodSection(detailLevel, x, z, data); + public boolean load(RenderDataSource renderDataSource) { + if (isLoaded()) throw new IllegalStateException("LodSection is already loaded"); + levelContainer = renderDataSource.createRenderData(pos); + return levelContainer != null; + } + public void unload() { + if (!isLoaded()) throw new IllegalStateException("LodSection is not loaded"); + levelContainer = null; } - + public boolean isLoaded() { + return levelContainer != null; + } } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContaioner.java b/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java similarity index 98% rename from src/main/java/com/seibel/lod/core/objects/a7/RenderDataContaioner.java rename to src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java index 0d6ebbf70..95066f2e0 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContaioner.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/RenderDataContainer.java @@ -16,7 +16,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; -public class RenderDataContaioner +public class RenderDataContainer { public static final boolean DO_SAFETY_CHECKS = true; @@ -27,7 +27,7 @@ public class RenderDataContaioner public final long[] dataContainer; - public RenderDataContaioner(byte detailLevel) + public RenderDataContainer(byte detailLevel) { this.detailLevel = detailLevel; verticalSize = DetailDistanceUtil.getMaxVerticalData(detailLevel); @@ -230,7 +230,7 @@ public class RenderDataContaioner } } - public RenderDataContaioner(DataInputStream inputData, int version, byte expectedDetailLevel) throws IOException { + public RenderDataContainer(DataInputStream inputData, int version, byte expectedDetailLevel) throws IOException { minHeight = SingletonHandler.get(IMinecraftClientWrapper.class).getWrappedClientWorld().getMinHeight(); detailLevel = inputData.readByte(); if (detailLevel != expectedDetailLevel) diff --git a/src/main/java/com/seibel/lod/core/objects/a7/RenderDataSource.java b/src/main/java/com/seibel/lod/core/objects/a7/RenderDataSource.java new file mode 100644 index 000000000..d2cb0c8e2 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/RenderDataSource.java @@ -0,0 +1,7 @@ +package com.seibel.lod.core.objects.a7; + +import com.seibel.lod.core.objects.a7.pos.DhSectionPos; + +public interface RenderDataSource { + RenderDataContainer createRenderData(DhSectionPos pos); +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/CompleteDataContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/data/CompleteDataContainer.java new file mode 100644 index 000000000..6d8d51c62 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/CompleteDataContainer.java @@ -0,0 +1,42 @@ +package com.seibel.lod.core.objects.a7.data; + +import com.seibel.lod.core.objects.a7.IdMappingUtil; +import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.function.Function; + +public class CompleteDataContainer extends LodDataSource { // 1 chunk + ArrayList idMap; + + protected CompleteDataContainer() { + idMap = new ArrayList(); + } + + @Override + public Function getLatestLoader() { + return null; + } + + @Override + public T[] getData() { + return null; + } + + public static CompleteDataContainer createNewFromChunk(IChunkWrapper chunk) { + CompleteDataContainer dataContainer = new CompleteDataContainer(); + HashMap idMap = new HashMap(); + + idMap.put(IdMappingUtil.BLOCKSTATE_ID_AIR, 0); + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + int y = chunk.getMaxY(x, z); + String currentBlockState = IdMappingUtil.BLOCKSTATE_ID_AIR; + // FIXME: Move LodBuilder code to here + } + } + return dataContainer; + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/DataHandler.java b/src/main/java/com/seibel/lod/core/objects/a7/data/DataHandler.java new file mode 100644 index 000000000..6d80a4a97 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/DataHandler.java @@ -0,0 +1,22 @@ +package com.seibel.lod.core.objects.a7.data; + +import com.seibel.lod.core.objects.a7.RenderDataContainer; +import com.seibel.lod.core.objects.a7.RenderDataSource; + +import java.io.File; + +public class DataHandler implements RenderDataSource { + public final File folder; + + public DataHandler(File folderPath) { + this.folder = folderPath; + } + + @Override + public RenderDataContainer createRenderData(byte detailLevel, int x, int z) { + + + //TODO + return null; + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataContainer.java deleted file mode 100644 index a56f02074..000000000 --- a/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataContainer.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.seibel.lod.core.objects.a7.data; - -import java.util.ArrayList; - -public class LodDataContainer { - ArrayList idMap; - int minX; - int minY; - int minZ; - int maxX; - int maxY; - int maxZ; - - public LodDataContainer() { - idMap = new ArrayList(); - } - - - - - - -} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java b/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java new file mode 100644 index 000000000..89ae563f0 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/data/LodDataSource.java @@ -0,0 +1,36 @@ +package com.seibel.lod.core.objects.a7.data; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.function.Function; + +public abstract class LodDataSource { + private static final String REGISTER_STRING_FILTER_REGEX = "^[a-zA-Z0-9_]*$"; + public static final HashMap> + dataSourceLoaderRegistry = new HashMap>(); + + public static void registerDataSourceLoader(String name, int version, Function loader) { + if (name == null || loader == null || name.isEmpty()) { + throw new IllegalArgumentException("Name and loader must be non-null, and not empty"); + } + if (!name.matches(REGISTER_STRING_FILTER_REGEX)) { + throw new IllegalArgumentException("Name must pass the regex " + REGISTER_STRING_FILTER_REGEX); + } + if (dataSourceLoaderRegistry.containsKey(name)) { + throw new IllegalArgumentException("Data source loader already registered for " + name); + } + dataSourceLoaderRegistry.put(name, loader); + } + + public static LodDataSource loadData(String dataSourceTypeName, ByteBuffer data) { + + Function loader = dataSourceLoaderRegistry.get(dataSourceTypeName); + if (loader == null) { + throw new IllegalArgumentException("No loader for data source type " + dataSourceTypeName); + } + return loader.apply(data); + } + public abstract Function getLatestLoader(); + + public abstract T[] getData(); //TODO & FIXME: What is T? +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/pos/DhBlockPos2D.java b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhBlockPos2D.java new file mode 100644 index 000000000..2161343dc --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhBlockPos2D.java @@ -0,0 +1,34 @@ +package com.seibel.lod.core.objects.a7.pos; + +import com.seibel.lod.core.objects.Pos2D; +import com.seibel.lod.core.util.LodUtil; + +public class DhBlockPos2D { + public final int x; + public final int z; + public DhBlockPos2D(int x, int z) { + this.x = x; + this.z = z; + } + + public DhBlockPos2D add(DhBlockPos2D other) { + return new DhBlockPos2D(x + other.x, z + other.z); + } + public DhBlockPos2D subtract(DhBlockPos2D other) { + return new DhBlockPos2D(x - other.x, z - other.z); + } + public double dist(DhBlockPos2D other) { + return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2)); + } + public long distSquared(DhBlockPos2D other) { + return LodUtil.pow2((long)x - other.x) + LodUtil.pow2((long)z - other.z); + } + + public Pos2D toPos2D() { + return new Pos2D(x, z); + } + + public static DhBlockPos2D fromPos2D(Pos2D pos) { + return new DhBlockPos2D(pos.x, pos.y); + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/pos/DhLodPos.java b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhLodPos.java new file mode 100644 index 000000000..5317426d2 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhLodPos.java @@ -0,0 +1,41 @@ +package com.seibel.lod.core.objects.a7.pos; + +import com.seibel.lod.core.objects.DHBlockPos; + +public class DhLodPos { + 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 "DhLodPos(" + 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 static int getWidth(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()); + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/pos/DhLodUnit.java b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhLodUnit.java new file mode 100644 index 000000000..f3b880e5a --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhLodUnit.java @@ -0,0 +1,29 @@ +package com.seibel.lod.core.objects.a7.pos; + +public class DhLodUnit { + public final byte detail; + public final int value; + + 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, block << targetDetail); + } + + public DhLodUnit convertTo(byte targetDetail) { + if (detail == targetDetail) { + return this; + } + if (detail > targetDetail) { //TODO check if this is correct + return new DhLodUnit(targetDetail, value >> (detail - targetDetail)); + } + return new DhLodUnit(targetDetail, value << (targetDetail - detail)); + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java new file mode 100644 index 000000000..1c5eecffb --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java @@ -0,0 +1,52 @@ +package com.seibel.lod.core.objects.a7.pos; + +import com.seibel.lod.core.objects.a7.DHLevel; +import org.lwjgl.system.CallbackI; + +import java.util.function.Consumer; + +public class DhSectionPos { + public static final int DATA_WIDTH_PER_SECTION = 64; + + public final byte detail; + public final int x; + public final int z; + + public DhSectionPos(byte detail, int x, int z) { + this.detail = detail; + this.x = x; + this.z = z; + } + + public DhLodPos getCenter() { + return new DhLodPos(detail, x * DATA_WIDTH_PER_SECTION + DATA_WIDTH_PER_SECTION / 2, z * DATA_WIDTH_PER_SECTION + DATA_WIDTH_PER_SECTION / 2); + } + + public DhLodPos getCorner() { + return new DhLodPos(detail, x * DATA_WIDTH_PER_SECTION, z * DATA_WIDTH_PER_SECTION); + } + + public DhLodUnit getWidth() { + return new DhLodUnit(detail, DATA_WIDTH_PER_SECTION); + } + + public static DhLodUnit getWidth(byte detail) { + return new DhLodUnit(detail, DATA_WIDTH_PER_SECTION); + } + + public DhSectionPos 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 DhSectionPos((byte) (detail - 1), x * 2 + (child0to3 & 1), z * 2 + (child0to3 & 2) / 2); + } + + public void forEachChild(Consumer callback){ + for (int i = 0; i < 4; i++) { + callback.accept(getChild(i)); + } + } + + public DhSectionPos getParent(){ + return new DhSectionPos((byte) (detail + 1), x / 2, z / 2); + } +} diff --git a/src/main/java/com/seibel/lod/core/util/LodUtil.java b/src/main/java/com/seibel/lod/core/util/LodUtil.java index db4c8eb05..cfc6b7d9e 100644 --- a/src/main/java/com/seibel/lod/core/util/LodUtil.java +++ b/src/main/java/com/seibel/lod/core/util/LodUtil.java @@ -338,6 +338,14 @@ public class LodUtil 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 int computeOverdrawOffset(LodDimension lodDim) { int chunkRenderDist = MC_RENDER.getRenderDistance() + 1; VanillaOverdraw overdraw = CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw(); @@ -397,6 +405,7 @@ public class LodUtil 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;} // True if the requested threshold pass, or false otherwise // For details, see: diff --git a/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java b/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java index 6f5736f70..0fc16ef69 100644 --- a/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java +++ b/src/main/java/com/seibel/lod/core/util/gridList/MovableGridRingList.java @@ -19,12 +19,15 @@ package com.seibel.lod.core.util.gridList; +import com.seibel.lod.core.objects.DHRegionPos; import com.seibel.lod.core.objects.Pos2D; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class MovableGridRingList extends ArrayList implements List { @@ -35,6 +38,28 @@ public class MovableGridRingList extends ArrayList implements List { private final int size; private final ReentrantReadWriteLock moveLock = new ReentrantReadWriteLock(); + private Pos2D[] ringIteratorList = null; + + //TODO: Check if this needs to be synchronized + private void buildRingIteratorList() { + ringIteratorList = null; + Pos2D[] list = new Pos2D[size*size]; + + int i = 0; + for (int ix=-halfSize; ix<=halfSize; ix++) { + for (int iz=-halfSize; iz<=halfSize; iz++) { + list[i] = new Pos2D(ix, iz); + i++; + } + } + Arrays.sort(list, (a, b) -> { + double disSqrA = a.x* a.x+ a.y* a.y; + double disSqrB = b.x* b.x+ b.y* b.y; + return Double.compare(disSqrA, disSqrB); + }); + ringIteratorList = list; + } + public MovableGridRingList(int halfSize, int centerX, int centerY) { super((halfSize * 2 + 1) * (halfSize * 2 + 1)); size = halfSize * 2 + 1; @@ -213,6 +238,57 @@ public class MovableGridRingList extends ArrayList implements List { } } + // TODO: Use MutablePos2D in the future + public void forEachPos(BiConsumer d) { + moveLock.readLock().lock(); + try { + Pos2D min = pos.get(); + for (int x = min.x; x < min.x + size; x++) { + for (int y = min.y; y < min.y + size; y++) { + T t = _getUnsafe(x, y); + d.accept(t, new Pos2D(x, y)); + } + } + } + finally { + moveLock.readLock().unlock(); + } + } + + // TODO: Use MutablePos2D in the future + public void forEachOrdered(Consumer d) { + if (ringIteratorList == null) buildRingIteratorList(); + moveLock.readLock().lock(); + try { + Pos2D min = pos.get(); + for (Pos2D offset : ringIteratorList) { + T t = _getUnsafe(min.x + offset.x, min.y + offset.y); + d.accept(t); + } + } + finally { + moveLock.readLock().unlock(); + } + } + + // TODO: Use MutablePos2D in the future + public void forEachPosOrdered(BiConsumer d) { + if (ringIteratorList == null) buildRingIteratorList(); + moveLock.readLock().lock(); + try { + Pos2D min = pos.get(); + for (Pos2D offset : ringIteratorList) { + T t = _getUnsafe(min.x + offset.x, min.y + offset.y); + d.accept(t, new Pos2D(min.x + offset.x, min.y + offset.y)); + } + } + finally { + moveLock.readLock().unlock(); + } + } + + + @Override public String toString() { Pos2D p = pos.get();