diff --git a/src/main/java/com/seibel/lod/QuadTreeImage.java b/src/main/java/com/seibel/lod/QuadTreeImage.java index 7217048f1..e333dd81f 100644 --- a/src/main/java/com/seibel/lod/QuadTreeImage.java +++ b/src/main/java/com/seibel/lod/QuadTreeImage.java @@ -30,7 +30,6 @@ import java.awt.event.ActionListener; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -43,11 +42,17 @@ import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.objects.LodDataPoint; import com.seibel.lod.objects.LodQuadTreeDimension; import com.seibel.lod.objects.LodQuadTreeNode; +import com.seibel.lod.objects.RegionPos; import com.seibel.lod.util.BiomeColorsUtils; import kaptainwutax.biomeutils.source.OverworldBiomeSource; import kaptainwutax.mcutils.version.MCVersion; +/** + * + * @author Leonardo Amato + * + */ @SuppressWarnings("serial") public class QuadTreeImage extends JPanel { @@ -122,7 +127,7 @@ public class QuadTreeImage extends JPanel int playerZ = 0 + playerZs[pos]/2; //int sizeOfTheWorld=512; //TRY THIS TO SEE A 250'000 BLOCK RENDER DISTANCE - dim.move(Math.floorDiv(playerX, 512), Math.floorDiv(playerZ, 512)); + dim.move(new RegionPos(Math.floorDiv(playerX, 512), Math.floorDiv(playerZ, 512))); /* System.out.println(dim.getRegion(0, 0)); System.out.println(dim.getCenterX()); diff --git a/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java b/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java index 59e239442..a9f0f4227 100644 --- a/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java +++ b/src/main/java/com/seibel/lod/handlers/LodQuadTreeDimensionFileHandler.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import com.seibel.lod.objects.LodQuadTree; +import com.seibel.lod.objects.LodQuadTreeRegion; import com.seibel.lod.objects.LodQuadTreeDimension; import com.seibel.lod.objects.LodQuadTreeNode; import com.seibel.lod.proxy.ClientProxy; @@ -98,7 +98,7 @@ public class LodQuadTreeDimensionFileHandler { * Return the LodQuadTree region at the given coordinates. * (null if the file doesn't exist) */ - public LodQuadTree loadRegionFromFile(int regionX, int regionZ) + public LodQuadTreeRegion loadRegionFromFile(int regionX, int regionZ) { String fileName = getFileNameAndPathForRegion(regionX, regionZ); @@ -195,7 +195,7 @@ public class LodQuadTreeDimensionFileHandler { // problem reading the file return null; } - return new LodQuadTree(dataList,regionX, regionZ); + return new LodQuadTreeRegion(dataList,regionX, regionZ); } @@ -240,7 +240,7 @@ public class LodQuadTreeDimensionFileHandler { * 2. This will save to the LodDimension that this * handler is associated with. */ - private void saveRegionToDisk(LodQuadTree region) + private void saveRegionToDisk(LodQuadTreeRegion region) { // convert chunk coordinates to region // coordinates @@ -306,7 +306,7 @@ public class LodQuadTreeDimensionFileHandler { fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n"); // add each LodChunk to the file - for (LodQuadTreeNode lodQuadTreeNode : Collections.unmodifiableList(region.getNodeList(LodQuadTreeDimension.FULL_COMPLEXITY_MASK , true, true))) + for (LodQuadTreeNode lodQuadTreeNode : Collections.unmodifiableList(region.getNodeListWithMask(LodQuadTreeDimension.FULL_COMPLEXITY_MASK , true, true))) { fw.write(lodQuadTreeNode.toData() + "\n"); lodQuadTreeNode.dirty = false; diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java index 214721f4b..21650f4b1 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java @@ -32,26 +32,36 @@ import com.seibel.lod.handlers.LodQuadTreeDimensionFileHandler; import com.seibel.lod.util.LodUtil; import net.minecraft.client.Minecraft; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.DimensionType; import net.minecraft.world.server.ServerChunkProvider; import net.minecraft.world.server.ServerWorld; +/** + * This object holds all loaded LOD regions + * for a given dimension. + * + * @author Leonardo Amato + * @author James Seibel + * @version 8-7-2021 + */ public class LodQuadTreeDimension { - - /**TODO a dimension should support two different type of quadTree. - * The ones that are near from the player should always be saved and can be fully generated (even at block level) - * The ones that are far from the player should always be non-savable and at a high level - * If this is not done then you could see how heavy a fully generated 64 region dimension can get. - * IDEA : use a mask like the "isRegionDirty" to achieve this*/ - + /**TODO a dimension should support two different type of quadTree. + * The ones that are near from the player should always be saved and can be fully generated (even at block level) + * The ones that are far from the player should always be non-savable and at a high level + * If this is not done then you could see how heavy a fully generated 64 region dimension can get. + * IDEA : use a mask like the "isRegionDirty" to achieve this*/ + public final DimensionType dimension; + /** measured in regions */ private volatile int width; + /** measured in regions */ private volatile int halfWidth; - public long seed; + /** */ public static final Set FULL_COMPLEXITY_MASK = new HashSet(); static { @@ -64,493 +74,496 @@ public class LodQuadTreeDimension } - public volatile LodQuadTree regions[][]; + public volatile LodQuadTreeRegion regions[][]; public volatile boolean isRegionDirty[][]; - private volatile int centerX; - private volatile int centerZ; + /** a chunk Position */ + private volatile RegionPos center; private LodQuadTreeDimensionFileHandler fileHandler; - - - public LodQuadTreeDimension(DimensionType newDimension, LodQuadTreeWorld lodWorld, int newMaxWidth) - { - dimension = newDimension; - width = newMaxWidth; - if(newDimension != null && lodWorld != null) { - try { - Minecraft mc = Minecraft.getInstance(); - - File saveDir; - if (mc.hasSingleplayerServer()) { - // local world - - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension); - seed = serverWorld.getSeed(); - // provider needs a separate variable to prevent - // the compiler from complaining - ServerChunkProvider provider = serverWorld.getChunkSource(); - saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); - } - else - { - // connected to server - - saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() + - File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level)); - } - - fileHandler = new LodQuadTreeDimensionFileHandler(saveDir, this); - - } - catch (IOException e) - { - // the file handler wasn't able to be created - // we won't be able to read or write any files - } - } - - - regions = new LodQuadTree[width][width]; - isRegionDirty = new boolean[width][width]; - - // populate isRegionDirty - for(int i = 0; i < width; i++) - for(int j = 0; j < width; j++) - isRegionDirty[i][j] = false; - - centerX = 0; - centerZ = 0; - - halfWidth = (int)Math.floor(width / 2); - } - - - /** - * Move the center of this LodDimension and move all owned - * regions over by the given x and z offset. - */ - public synchronized void move(int xOffset, int zOffset) - { - // if the x or z offset is equal to or greater than - // the total size, just delete the current data - // and update the centerX and/or centerZ - if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width) - { - for(int x = 0; x < width; x++) - { - for(int z = 0; z < width; z++) - { - regions[x][z] = null; - } - } - - // update the new center - centerX += xOffset; - centerZ += zOffset; - - return; - } - - - // X - if(xOffset > 0) - { - // move everything over to the left (as the center moves to the right) - for(int x = 0; x < width; x++) - { - for(int z = 0; z < width; z++) - { - if(x + xOffset < width) - regions[x][z] = regions[x + xOffset][z]; - else - regions[x][z] = null; - } - } - } - else - { - // move everything over to the right (as the center moves to the left) - for(int x = width - 1; x >= 0; x--) - { - for(int z = 0; z < width; z++) - { - if(x + xOffset >= 0) - regions[x][z] = regions[x + xOffset][z]; - else - regions[x][z] = null; - } - } - } - - - - // Z - if(zOffset > 0) - { - // move everything up (as the center moves down) - for(int x = 0; x < width; x++) - { - for(int z = 0; z < width; z++) - { - if(z + zOffset < width) - regions[x][z] = regions[x][z + zOffset]; - else - regions[x][z] = null; - } - } - } - else - { - // move everything down (as the center moves up) - for(int x = 0; x < width; x++) - { - for(int z = width - 1; z >= 0; z--) - { - if(z + zOffset >= 0) - regions[x][z] = regions[x][z + zOffset]; - else - regions[x][z] = null; - } - } - } - - - - // update the new center - centerX += xOffset; - centerZ += zOffset; - } - - - - - - - /** - * Gets the region at the given X and Z - *
- * Returns null if the region doesn't exist - * or is outside the loaded area. - */ - public LodQuadTree getRegion(int regionX, int regionZ) - { - int xIndex = (regionX - centerX) + halfWidth; - int zIndex = (regionZ - centerZ) + halfWidth; - - if (!regionIsInRange(regionX, regionZ)) - // out of range - return null; - - if (regions[xIndex][zIndex] == null) - { - regions[xIndex][zIndex] = getRegionFromFile(regionX, regionZ); - if (regions[xIndex][zIndex] == null) - { - regions[xIndex][zIndex] = new LodQuadTree(regionX, regionZ); - } - } - - return regions[xIndex][zIndex]; - } - - /** - * Overwrite the LodRegion at the location of newRegion with newRegion. - * @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension. - */ - public void addOrOverwriteRegion(LodQuadTree newRegion) throws ArrayIndexOutOfBoundsException - { - int xIndex = (newRegion.getLodNodeData().posX - centerX) + halfWidth; - int zIndex = (centerZ - newRegion.getLodNodeData().posZ) + halfWidth; - - if (!regionIsInRange(newRegion.getLodNodeData().posX, newRegion.getLodNodeData().posZ)) - // out of range - throw new ArrayIndexOutOfBoundsException(); - - regions[xIndex][zIndex] = newRegion; - } - - - /** - *this method create all the regions that are null - */ - public void initializeNullRegions() - { - int n = regions.length; - int xIndex; - int zIndex; - LodQuadTree region; - for(int xRegion=0; xRegion - * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public LodQuadTreeNode getLodFromCoordinates(ChunkPos chunkPos) - { - return getLodFromCoordinates(chunkPos.x, chunkPos.z, LodQuadTreeNode.CHUNK_LEVEL); - } - - /** - * Get the LodNodeData at the given X and Z coordinates - * in this dimension. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public LodQuadTreeNode getLodFromCoordinates(int chunkPosX, int chunkPosZ) - { - return getLodFromCoordinates(chunkPosX, chunkPosZ, LodQuadTreeNode.CHUNK_LEVEL); - } - - /** - * Get the LodNodeData at the given X and Z coordinates - * in this dimension. - *
- * Returns null if the LodChunk doesn't exist or - * is outside the loaded area. - */ - public LodQuadTreeNode getLodFromCoordinates(int chunkPosX, int chunkPosZ, int detailLevel) - { - if (detailLevel > LodQuadTreeNode.REGION_LEVEL) - throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); - - RegionPos regionPos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(chunkPosX, chunkPosZ)); - LodQuadTree region = getRegion(regionPos.x, regionPos.z); - - if(region == null) - { - //System.out.println("THIS CASE"); - return null; - } - - return region.getNodeAtChunkPos(chunkPosX, chunkPosZ, detailLevel); - - /* - RegionPos pos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ)); + /** + * Creates the dimension centered at (0,0) + * + * @param newWidth in regions + */ + public LodQuadTreeDimension(DimensionType newDimension, LodQuadTreeWorld lodWorld, int newWidth) + { + if(newDimension != null && lodWorld != null) + throw new IllegalArgumentException("newDimension and/or lodWorld are null"); - LodQuadTree region = getRegion(pos.x, pos.z); + dimension = newDimension; + width = newWidth; + halfWidth = (int)Math.floor(width / 2); - return region.getNode(chunkX, chunkZ); - */ - } - - /** - * return true if and only if the node at that position exist - */ - public boolean hasThisPositionBeenGenerated(int posX, int posZ, int level) - { - if (level > LodQuadTreeNode.REGION_LEVEL) - throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + level + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); - - return getLodFromCoordinates(posX,posZ,level).detailLevel == level; - } - - /** - * method to get all the nodes that have to be rendered based on the position of the player - * @return list of nodes - */ - public List getNodeToRender(int x, int z, int level, Set complexityMask, int maxDistance, int minDistance) - { - int n = regions.length; - List listOfData = new ArrayList<>(); - for(int i=0; i getNodesToGenerate(int x, int z, byte level, DistanceGenerationMode complexity, int maxDistance, int minDistance) - { - int n = regions.length; - int xIndex; - int zIndex; - LodQuadTree region; - List> listOfQuadTree = new ArrayList<>(); - for(int xRegion=0; xRegion entry.getKey()).collect(Collectors.toList()); - } - - /** - * getNodes - * @return list of quadTrees - */ - public List getNodes(Set complexityMask, boolean getOnlyDirty, boolean getOnlyLeaf) - { - int n = regions.length; - List listOfNodes = new ArrayList<>(); - int xIndex; - int zIndex; - LodQuadTree region; - for(int xRegion=0; xRegion= 0 && xIndex < width && zIndex >= 0 && zIndex < width; - } + + try { + Minecraft mc = Minecraft.getInstance(); + + File saveDir; + if (mc.hasSingleplayerServer()) + { + // local world + + ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(newDimension); + + // provider needs a separate variable to prevent + // the compiler from complaining + ServerChunkProvider provider = serverWorld.getChunkSource(); + saveDir = new File(provider.dataStorage.dataFolder.getCanonicalFile().getPath() + File.separatorChar + "lod"); + } + else + { + // connected to server + + saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() + + File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level)); + } + + fileHandler = new LodQuadTreeDimensionFileHandler(saveDir, this); + } + catch (IOException e) + { + // the file handler wasn't able to be created + // we won't be able to read or write any files + } + + + + regions = new LodQuadTreeRegion[width][width]; + isRegionDirty = new boolean[width][width]; + + // populate isRegionDirty + for(int i = 0; i < width; i++) + for(int j = 0; j < width; j++) + isRegionDirty[i][j] = false; + + center = new RegionPos(0,0); + } - - - - - - public int getCenterX() - { - return centerX; - } - - public int getCenterZ() - { - return centerZ; - } - - - /** - * TODO THIS METHOD HAVE TO BE CHANGES. IS NOT THE SAME AS NUMER OF CHUNK - * Is it good now? - James - * - * Returns how many non-null LodChunks - * are stored in this LodDimension. - */ - public int getNumberOfLods() - { - int numbLods = 0; - for (LodQuadTree[] regions : regions) - { - if(regions == null) - continue; - - for (LodQuadTree region : regions) - { - if(region == null) - continue; - - for(LodQuadTreeNode node : region.getNodeList(FULL_COMPLEXITY_MASK,false,true)) + /** + * Move the center of this LodDimension and move all owned + * regions over by the given x and z offset. + */ + public synchronized void move(RegionPos regionOffset) + { + int xOffset = regionOffset.x; + int zOffset = regionOffset.z; + + // if the x or z offset is equal to or greater than + // the total size, just delete the current data + // and update the centerX and/or centerZ + if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width) + { + for(int x = 0; x < width; x++) + { + for(int z = 0; z < width; z++) { - if (node != null && !node.voidNode) - numbLods++; + regions[x][z] = null; } - } - } + } + + // update the new center + center.x += xOffset; + center.z += zOffset; + + return; + } - return numbLods; - } - - - public int getWidth() - { + + // X + if(xOffset > 0) + { + // move everything over to the left (as the center moves to the right) + for(int x = 0; x < width; x++) + { + for(int z = 0; z < width; z++) + { + if(x + xOffset < width) + regions[x][z] = regions[x + xOffset][z]; + else + regions[x][z] = null; + } + } + } + else + { + // move everything over to the right (as the center moves to the left) + for(int x = width - 1; x >= 0; x--) + { + for(int z = 0; z < width; z++) + { + if(x + xOffset >= 0) + regions[x][z] = regions[x + xOffset][z]; + else + regions[x][z] = null; + } + } + } + + + + // Z + if(zOffset > 0) + { + // move everything up (as the center moves down) + for(int x = 0; x < width; x++) + { + for(int z = 0; z < width; z++) + { + if(z + zOffset < width) + regions[x][z] = regions[x][z + zOffset]; + else + regions[x][z] = null; + } + } + } + else + { + // move everything down (as the center moves up) + for(int x = 0; x < width; x++) + { + for(int z = width - 1; z >= 0; z--) + { + if(z + zOffset >= 0) + regions[x][z] = regions[x][z + zOffset]; + else + regions[x][z] = null; + } + } + } + + + + // update the new center + center.x += xOffset; + center.z += zOffset; + } + + + + + + + /** + * Gets the region at the given X and Z + *
+ * Returns null if the region doesn't exist + * or is outside the loaded area. + */ + public LodQuadTreeRegion getRegion(RegionPos regionPos) + { + int xIndex = (regionPos.x - center.x) + halfWidth; + int zIndex = (regionPos.z - center.z) + halfWidth; + + if (!regionIsInRange(regionPos.x, regionPos.z)) + // out of range + return null; + + if (regions[xIndex][zIndex] == null) + { + regions[xIndex][zIndex] = getRegionFromFile(regionPos.x, regionPos.z); + if (regions[xIndex][zIndex] == null) + { + regions[xIndex][zIndex] = new LodQuadTreeRegion(regionPos.x, regionPos.z); + } + } + + return regions[xIndex][zIndex]; + } + + /** + * Overwrite the LodRegion at the location of newRegion with newRegion. + * + * @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension. + */ + public void addOrOverwriteRegion(LodQuadTreeRegion newRegion) throws ArrayIndexOutOfBoundsException + { + int xIndex = (newRegion.getLodNodeData().posX - center.x) + halfWidth; + int zIndex = (center.z - newRegion.getLodNodeData().posZ) + halfWidth; + + if (!regionIsInRange(newRegion.getLodNodeData().posX, newRegion.getLodNodeData().posZ)) + // out of range + throw new ArrayIndexOutOfBoundsException(); + + regions[xIndex][zIndex] = newRegion; + } + + + /** + *this method creates all null regions + */ + public void initializeNullRegions() + { + int regionX; + int regionZ; + LodQuadTreeRegion region; + + for(int x = 0; x < regions.length; x++) + { + for(int z = 0; z < regions.length; z++) + { + regionX = (x + center.x) - halfWidth; + regionZ = (z + center.z) - halfWidth; + region = getRegion(new RegionPos(regionX,regionZ)); + + if (region == null) + { + // if no region exists, create it + region = new LodQuadTreeRegion(regionX, regionZ); + addOrOverwriteRegion(region); + } + } + } + } + + + /** + * Add the given LOD to this dimension at the coordinate + * stored in the LOD. If an LOD already exists at the given + * coordinates it will be overwritten. + */ + public Boolean addNode(LodQuadTreeNode lodNode) + { + RegionPos regionPos = LodUtil.convertChunkPosToRegionPos(new ChunkPos(lodNode.centerX, lodNode.centerZ)); + + // don't continue if the region can't be saved + if (!regionIsInRange(regionPos.x, regionPos.z)) + { + return false; + } + + LodQuadTreeRegion region = getRegion(new RegionPos(regionPos.x, regionPos.z)); + + if (region == null) + { + // if no region exists, create it + region = new LodQuadTreeRegion(regionPos.x, regionPos.z); + addOrOverwriteRegion(region); + } + boolean nodeAdded = region.setNodeAtLowerLevel(lodNode, true); + + // only save valid LODs to disk + if (!lodNode.dontSave && fileHandler != null) + { + // mark the region as dirty so it will be saved to disk + int xIndex = (regionPos.x - center.x) + halfWidth; + int zIndex = (regionPos.z - center.z) + halfWidth; + isRegionDirty[xIndex][zIndex] = true; + fileHandler.saveDirtyRegionsToFileAsync(); + } + return nodeAdded; + } + + + /** + * Get the LodNodeData at the given X and Z coordinates + * in this dimension. + *
+ * Returns null if the LodChunk doesn't exist or + * is outside the loaded area. + */ + public LodQuadTreeNode getLodFromCoordinates(ChunkPos chunkPos) + { + return getLodFromCoordinates(chunkPos, LodQuadTreeNode.CHUNK_LEVEL); + } + + /** + * Get the LodNodeData at the given X and Z coordinates + * in this dimension. + *
+ * Returns null if the LodChunk doesn't exist or + * is outside the loaded area. + */ + public LodQuadTreeNode getLodFromCoordinates(ChunkPos chunkPos, int detailLevel) + { + if (detailLevel > LodQuadTreeNode.REGION_LEVEL) + throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); + + RegionPos regionPos = LodUtil.convertChunkPosToRegionPos(chunkPos); + LodQuadTreeRegion region = getRegion(regionPos); + + if(region == null) + { + return null; + } + + return region.getNodeAtChunkPos(chunkPos.x, chunkPos.z, detailLevel); + } + + /** + * return true if and only if the node at that position exist + */ + public boolean hasThisPositionBeenGenerated(ChunkPos chunkPos, int level) + { + if (level > LodQuadTreeNode.REGION_LEVEL) + throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + level + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); + + return getLodFromCoordinates(chunkPos, level).detailLevel == level; + } + + /** + * method to get all the nodes that have to be rendered based on the position of the player + * @return list of nodes + */ + public List getNodeToRender(BlockPos playerPos, int detailLevel, + Set complexityMask, int maxDistance, int minDistance) + { + List listOfData = new ArrayList<>(); + + // go through every region we have stored + for(int i = 0; i < regions.length; i++) + { + for(int j = 0; j < regions.length; j++) + { + listOfData.addAll(regions[i][j].getNodeToRender(playerPos.getX(), playerPos.getZ(), detailLevel, complexityMask, maxDistance, minDistance)); + } + } + + return listOfData; + } + + /** + * method to get all the quadtree levels that have to be generated based on the position of the player + * @return list of quadTrees + */ + public List getNodesToGenerate(BlockPos playerPos, byte level, DistanceGenerationMode complexity, int maxDistance, int minDistance) + { + int regionX; + int regionZ; + LodQuadTreeRegion region; + List> listOfQuadTree = new ArrayList<>(); + + // go through every region we have stored + for(int xIndex = 0; xIndex < regions.length; xIndex++) + { + for(int zIndex = 0; zIndex < regions.length; zIndex++) + { + regionX = (xIndex + center.x) - halfWidth; + regionZ = (zIndex + center.z) - halfWidth; + region = getRegion(new RegionPos(regionX,regionZ)); + + if (region == null) + { + region = new LodQuadTreeRegion(regionX, regionZ); + addOrOverwriteRegion(region); + } + + listOfQuadTree.addAll(region.getNodesToGenerate(playerPos.getX(), playerPos.getZ(), level, complexity, maxDistance, minDistance)); + } + } + + Collections.sort(listOfQuadTree,Map.Entry.comparingByValue()); + return listOfQuadTree.stream().map(entry -> entry.getKey()).collect(Collectors.toList()); + } + + /** + * @see LodQuadTreeRegion#getNodeListWithMask + */ + public List getNodesWithMask(Set complexityMask, boolean getOnlyDirty, boolean getOnlyLeaf) + { + List listOfNodes = new ArrayList<>(); + + int xIndex; + int zIndex; + LodQuadTreeRegion region; + + // go through every region we have stored + for(int xRegion = 0; xRegion < regions.length; xRegion++) + { + for(int zRegion = 0; zRegion < regions.length; zRegion++) + { + xIndex = (xRegion + center.x) - halfWidth; + zIndex = (zRegion + center.z) - halfWidth; + region = getRegion(new RegionPos(xIndex,zIndex)); + + if (region != null) + { + listOfNodes.addAll(region.getNodeListWithMask(complexityMask, getOnlyDirty, getOnlyLeaf)); + } + } + } + return listOfNodes; + } + + /** + * Get the region at the given X and Z coordinates from the + * RegionFileHandler. + */ + public LodQuadTreeRegion getRegionFromFile(int regionX, int regionZ) + { + if (fileHandler != null) + return fileHandler.loadRegionFromFile(regionX, regionZ); + else + return null; + } + + + /** + * Returns whether the region at the given X and Z coordinates + * is within the loaded range. + */ + public boolean regionIsInRange(int regionX, int regionZ) + { + int xIndex = (regionX - center.x) + halfWidth; + int zIndex = (regionZ - center.z) + halfWidth; + + return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width; + } + + + + + + + + public int getCenterX() + { + return center.x; + } + + public int getCenterZ() + { + return center.z; + } + + + /** + * TODO THIS METHOD HAVE TO BE CHANGES. IS NOT THE SAME AS NUMER OF CHUNK + * Is it good now? - James + * + * Returns how many non-null LodChunks + * are stored in this LodDimension. + */ + public int getNumberOfLods() + { + int numbLods = 0; + for (LodQuadTreeRegion[] regions : regions) + { + if(regions == null) + continue; + + for (LodQuadTreeRegion region : regions) + { + if(region == null) + continue; + + for(LodQuadTreeNode node : region.getNodeListWithMask(FULL_COMPLEXITY_MASK,false,true)) + { + if (node != null && !node.voidNode) + numbLods++; + } + } + } + + return numbLods; + } + + + public int getWidth() + { if (regions != null) { // we want to get the length directly from the @@ -560,33 +573,33 @@ public class LodQuadTreeDimension } else { - return width; - } + return width; + } + } + + public void setRegionWidth(int newWidth) + { + width = newWidth; + halfWidth = (int)Math.floor(width / 2); + + regions = new LodQuadTreeRegion[width][width]; + isRegionDirty = new boolean[width][width]; + + // populate isRegionDirty + for(int i = 0; i < width; i++) + for(int j = 0; j < width; j++) + isRegionDirty[i][j] = false; + } + + + @Override + public String toString() + { + String s = ""; + + s += "dim: " + dimension.toString() + "\t"; + s += "(" + center.x + "," + center.z + ")"; + + return s; } - - public void setRegionWidth(int newWidth) - { - width = newWidth; - halfWidth = (int)Math.floor(width / 2); - - regions = new LodQuadTree[width][width]; - isRegionDirty = new boolean[width][width]; - - // populate isRegionDirty - for(int i = 0; i < width; i++) - for(int j = 0; j < width; j++) - isRegionDirty[i][j] = false; - } - - - @Override - public String toString() - { - String s = ""; - - s += "dim: " + dimension.toString() + "\t"; - s += "(" + centerX + "," + centerZ + ")"; - - return s; - } } diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTree.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeRegion.java similarity index 92% rename from src/main/java/com/seibel/lod/objects/LodQuadTree.java rename to src/main/java/com/seibel/lod/objects/LodQuadTreeRegion.java index efafd31a7..4309505e0 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeRegion.java @@ -28,7 +28,8 @@ import com.seibel.lod.enums.DistanceGenerationMode; * This object contains all data useful to render LodBlock in a region (32x32 chunk o 512x512 block) * for every node it contains the border of the block, the size, the position at it's level, the color, the height and the depth. */ -public class LodQuadTree { +public class LodQuadTreeRegion +{ //notes //The term node correspond to a LodQuadTree object @@ -79,10 +80,10 @@ public class LodQuadTree { //the four child based on the four diagonal cardinal direction //the first index is for N and S and the second index is for W and S //children should always be null for level 0. - private final LodQuadTree[][] children; + private final LodQuadTreeRegion[][] children; //parent should always be null for level 9, and always not null for other levels. - private final LodQuadTree parent; + private final LodQuadTreeRegion parent; /** * Constructor for level 0 without LodNodeData (region level constructor) @@ -91,7 +92,8 @@ public class LodQuadTree { * @param regionZ indicate the z region position of the node */ //maybe the use of useLevelCoordinate could be changed. I could use a builder to do all this work. - public LodQuadTree(int regionX, int regionZ) { + public LodQuadTreeRegion(int regionX, int regionZ) + { this(null, new LodQuadTreeNode(LodQuadTreeNode.REGION_LEVEL, regionX, regionZ)); } @@ -103,7 +105,7 @@ public class LodQuadTree { * @param posX position x in the level * @param posZ position z in the level */ - public LodQuadTree(LodQuadTree parent, byte level, int posX, int posZ) { + public LodQuadTreeRegion(LodQuadTreeRegion parent, byte level, int posX, int posZ) { this(parent, new LodQuadTreeNode(level, posX, posZ)); } @@ -112,10 +114,10 @@ public class LodQuadTree { * * @param lodNode object containing all the information of this node */ - public LodQuadTree(LodQuadTree parent, LodQuadTreeNode lodNode) { + public LodQuadTreeRegion(LodQuadTreeRegion parent, LodQuadTreeNode lodNode) { this.parent = parent; this.lodNode = lodNode; - this.children = new LodQuadTree[2][2]; + this.children = new LodQuadTreeRegion[2][2]; this.nodeEmpty = true; this.nodeFull = false; } @@ -127,7 +129,7 @@ public class LodQuadTree { * @param regionX x region coordinate * @param regionZ z region coordinate */ - public LodQuadTree(List dataList, int regionX, int regionZ) { + public LodQuadTreeRegion(List dataList, int regionX, int regionZ) { this(null, new LodQuadTreeNode(LodQuadTreeNode.REGION_LEVEL, regionX, regionZ)); this.setNodesAtLowerLevel(dataList, true); } @@ -163,7 +165,7 @@ public class LodQuadTree { if (getChild(NS, WE) == null) { setChild(NS, WE); } - LodQuadTree child = getChild(NS, WE); + LodQuadTreeRegion child = getChild(NS, WE); if (lodNode.compareComplexity(newLodNode) > 0) { //the node we want to introduce is less complex than the current node @@ -215,7 +217,7 @@ public class LodQuadTree { { return null; } - LodQuadTree child = getChild(NS, WE); + LodQuadTreeRegion child = getChild(NS, WE); return child.getNodeAtChunkPos(chunkPosX, chunkPosZ, targetLevel); } else @@ -226,7 +228,7 @@ public class LodQuadTree { } - public LodQuadTree getChild(int NS, int WE) + public LodQuadTreeRegion getChild(int NS, int WE) { return children[NS][WE]; } @@ -240,7 +242,7 @@ public class LodQuadTree { */ public void setChild(LodQuadTreeNode newLodNode, int NS, int WE) { if (newLodNode.detailLevel == lodNode.detailLevel - 1) { - children[NS][WE] = new LodQuadTree(this, lodNode); + children[NS][WE] = new LodQuadTreeRegion(this, lodNode); } } @@ -253,7 +255,7 @@ public class LodQuadTree { if (newLodNode.detailLevel == lodNode.detailLevel - 1) { int WE = newLodNode.posX % lodNode.posX; int NS = newLodNode.posZ % lodNode.posZ; - children[NS][WE] = new LodQuadTree(this, lodNode); + children[NS][WE] = new LodQuadTreeRegion(this, lodNode); } } @@ -266,7 +268,7 @@ public class LodQuadTree { public void setChild(int NS, int WE) { int childX = lodNode.posX * 2 + WE; int childZ = lodNode.posZ * 2 + NS; - children[NS][WE] = new LodQuadTree(this, (byte) (lodNode.detailLevel - 1), childX, childZ); + children[NS][WE] = new LodQuadTreeRegion(this, (byte) (lodNode.detailLevel - 1), childX, childZ); } /** @@ -305,7 +307,7 @@ public class LodQuadTree { * @param getOnlyLeaf if true it will return only leaf nodes * @return list of nodes */ - public List getNodeList(Set complexityMask, boolean getOnlyDirty, boolean getOnlyLeaf) { + public List getNodeListWithMask(Set complexityMask, boolean getOnlyDirty, boolean getOnlyLeaf) { List nodeList = new ArrayList<>(); if (isThereAnyChild()) { //There is at least 1 child @@ -316,9 +318,9 @@ public class LodQuadTree { } for (int NS = 0; NS <= 1; NS++) { for (int WE = 0; WE <= 1; WE++) { - LodQuadTree child = children[NS][WE]; + LodQuadTreeRegion child = children[NS][WE]; if (child != null) { - nodeList.addAll(child.getNodeList(complexityMask, getOnlyDirty, getOnlyLeaf)); + nodeList.addAll(child.getNodeListWithMask(complexityMask, getOnlyDirty, getOnlyLeaf)); } } } @@ -363,7 +365,7 @@ public class LodQuadTree { } else { for (int NS = 0; NS <= 1; NS++) { for (int WE = 0; WE <= 1; WE++) { - LodQuadTree child = getChild(NS, WE); + LodQuadTreeRegion child = getChild(NS, WE); if (child != null) { nodeList.addAll(child.getNodeToRender(x, z, targetLevel, complexityMask, maxDistance, minDistance)); } diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTreeWorld.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeWorld.java index 3167e2918..78f0e60f4 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTreeWorld.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeWorld.java @@ -25,93 +25,116 @@ import net.minecraft.world.DimensionType; /** * This stores all LODs for a given world. * + * @author James Seibel + * @author Leonardo Amato + * @version 8-7-2021 */ public class LodQuadTreeWorld { - - private String worldName; - - private Map lodDimensions; - /** If true then the LOD world is setup and ready to use */ - private boolean isWorldLoaded = false; - - public static final String NO_WORLD_LOADED = "No world loaded"; - - public LodQuadTreeWorld() { - worldName = NO_WORLD_LOADED; - } - - /** - * Set up the LodWorld with the given newWorldName.
- * This should be done whenever loading a new world. - * @param newWorldName name of the world - */ - public void selectWorld(String newWorldName) { - if (newWorldName.isEmpty()) { - deselectWorld(); - return; - } - - if (worldName.equals(newWorldName)) - // don't recreate everything if we - // didn't actually change worlds - return; - - worldName = newWorldName; + private String worldName; + + private Map lodDimensions; + + /** If true then the LOD world is setup and ready to use */ + private boolean isWorldLoaded = false; + + public static final String NO_WORLD_LOADED = "No world loaded"; + + + + public LodQuadTreeWorld() + { + worldName = NO_WORLD_LOADED; + } + + /** + * Set up the LodQuadTreeWorld with the given newWorldName.
+ * This should be done whenever loading a new world. + * + * @param newWorldName name of the world + */ + public void selectWorld(String newWorldName) + { + if (newWorldName.isEmpty()) + { + deselectWorld(); + return; + } + + if (worldName.equals(newWorldName)) + // don't recreate everything if we + // didn't actually change worlds + return; + + worldName = newWorldName; lodDimensions = new Hashtable(); - isWorldLoaded = true; - } - - /** - * Set the worldName to "No world loaded" - * and clear the lodDimensions Map.
- * This should be done whenever unloaded a world. - */ - public void deselectWorld() { - worldName = NO_WORLD_LOADED; - lodDimensions = null; - isWorldLoaded = false; - } - - - public void addLodDimension(LodQuadTreeDimension newStorage) { - if (lodDimensions == null) - throw new IllegalStateException("LodWorld hasn't been given a world yet."); - - lodDimensions.put(newStorage.dimension, newStorage); - } - - public LodQuadTreeDimension getLodDimension(DimensionType dimension) { - if (lodDimensions == null) { - throw new IllegalStateException("LodWorld hasn't been given a world yet."); - } - return lodDimensions.get(dimension); - } - - /** - * Resizes the max width in regions that each LodDimension - * should use. - */ - public void resizeDimensionRegionWidth(int newWidth) { - if (lodDimensions == null) - throw new IllegalStateException("LodWorld hasn't been given a world yet."); - - for (DimensionType key : lodDimensions.keySet()) - lodDimensions.get(key).setRegionWidth(newWidth); - } - - - public boolean getIsWorldLoaded() { - return isWorldLoaded; - } - - public String getWorldName() { - return worldName; - } - - @Override - public String toString() { - return "World name: " + worldName; - } + isWorldLoaded = true; + } + + /** + * Set the worldName to "No world loaded" + * and clear the lodDimensions Map.
+ * This should be done whenever unloaded a world. + */ + public void deselectWorld() + { + worldName = NO_WORLD_LOADED; + lodDimensions = null; + isWorldLoaded = false; + } + + + /** + * Adds newStorage to this world, if a LodQuadTreeDimension + * already exists for the given dimension it is replaced. + */ + public void addLodDimension(LodQuadTreeDimension newStorage) + { + if (lodDimensions == null) + throw new IllegalStateException("LodWorld hasn't been given a world yet."); + + lodDimensions.put(newStorage.dimension, newStorage); + } + + /** + * Returns null if no LodQuadTreeDimension exists for the given dimension + */ + public LodQuadTreeDimension getLodDimension(DimensionType dimension) + { + if (lodDimensions == null) { + throw new IllegalStateException("LodWorld hasn't been given a world yet."); + } + return lodDimensions.get(dimension); + } + + /** + * Resizes the max width in regions that each LodDimension + * should use. + */ + public void resizeDimensionRegionWidth(int newWidth) + { + if (lodDimensions == null) + throw new IllegalStateException("LodWorld hasn't been given a world yet."); + + for (DimensionType key : lodDimensions.keySet()) + lodDimensions.get(key).setRegionWidth(newWidth); + } + + + public boolean getIsWorldLoaded() + { + return isWorldLoaded; + } + + public String getWorldName() + { + return worldName; + } + + @Override + public String toString() + { + return "World name: " + worldName; + } } diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 1900f8a28..12707922c 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -31,6 +31,7 @@ import com.seibel.lod.objects.LodChunk; import com.seibel.lod.objects.LodQuadTreeDimension; import com.seibel.lod.objects.LodQuadTreeWorld; import com.seibel.lod.objects.LodRegion; +import com.seibel.lod.objects.RegionPos; import com.seibel.lod.render.LodNodeRenderer; import com.seibel.lod.util.LodUtil; @@ -116,7 +117,7 @@ public class ClientProxy if (xOffset != 0 || zOffset != 0) { - lodDim.move(xOffset, zOffset); + lodDim.move(new RegionPos(xOffset, zOffset)); }