Continue a7 dev focused on qTree

This commit is contained in:
TomTheFurry
2022-05-07 16:58:38 +08:00
parent 037e890d0d
commit c4258ca96c
18 changed files with 509 additions and 60 deletions
@@ -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);
}
}
@@ -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;
}
}
}
@@ -0,0 +1,6 @@
package com.seibel.lod.core.objects.a7;
public class IdMappingUtil {
public static final String BLOCKSTATE_ID_AIR = "air";
//TODO HERE
}
@@ -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);
}
@@ -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<RenderDataContaioner>[] ringLists;
private final MovableGridRingList<LodSection>[] 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<RenderDataContaioner>(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<LodSection>(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<LodSection> ringList = ringLists[detailLevel];
final MovableGridRingList<LodSection> 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<LodSection> 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<LodSection> ringList = ringLists[detailLevel];
final byte detail = detailLevel;
ringList.forEachPosOrdered((r, pos) -> {
DhSectionPos sectionPos = new DhSectionPos(detail, pos.x, pos.y);
});
}
}
}
@@ -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;
}
}
@@ -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)
@@ -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);
}
@@ -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<String> idMap;
protected CompleteDataContainer() {
idMap = new ArrayList<String>();
}
@Override
public Function<ByteBuffer, ? extends LodDataSource> getLatestLoader() {
return null;
}
@Override
public <T> T[] getData() {
return null;
}
public static CompleteDataContainer createNewFromChunk(IChunkWrapper chunk) {
CompleteDataContainer dataContainer = new CompleteDataContainer();
HashMap<String, Integer> idMap = new HashMap<String, Integer>();
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;
}
}
@@ -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;
}
}
@@ -1,23 +0,0 @@
package com.seibel.lod.core.objects.a7.data;
import java.util.ArrayList;
public class LodDataContainer {
ArrayList<Integer> idMap;
int minX;
int minY;
int minZ;
int maxX;
int maxY;
int maxZ;
public LodDataContainer() {
idMap = new ArrayList<Integer>();
}
}
@@ -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<String, Function<ByteBuffer,? extends LodDataSource>>
dataSourceLoaderRegistry = new HashMap<String, Function<ByteBuffer,? extends LodDataSource>>();
public static void registerDataSourceLoader(String name, int version, Function<ByteBuffer,? extends LodDataSource> 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<ByteBuffer,? extends LodDataSource> loader = dataSourceLoaderRegistry.get(dataSourceTypeName);
if (loader == null) {
throw new IllegalArgumentException("No loader for data source type " + dataSourceTypeName);
}
return loader.apply(data);
}
public abstract Function<ByteBuffer,? extends LodDataSource> getLatestLoader();
public abstract <T> T[] getData(); //TODO & FIXME: What is T?
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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));
}
}
@@ -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<DhSectionPos> 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);
}
}
@@ -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:
@@ -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<T> extends ArrayList<T> implements List<T> {
@@ -35,6 +38,28 @@ public class MovableGridRingList<T> extends ArrayList<T> implements List<T> {
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<T> extends ArrayList<T> implements List<T> {
}
}
// TODO: Use MutablePos2D in the future
public void forEachPos(BiConsumer<? super T, Pos2D> 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<? super T> 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<? super T, Pos2D> 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();