From 193c57971225b541ee3e07bb46945117416e1830 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Mon, 30 Aug 2021 23:03:22 +0200 Subject: [PATCH] changed the generation. Added re-use of levelPos --- .../worldGeneration/LodWorldGenerator.java | 174 +++++++----------- .../com/seibel/lod/handlers/LodConfig.java | 4 +- .../seibel/lod/objects/LevelPos/LevelPos.java | 4 +- .../com/seibel/lod/objects/LodDimension.java | 21 +-- .../com/seibel/lod/objects/LodRegion.java | 59 +++--- .../com/seibel/lod/render/LodRenderer.java | 4 +- .../seibel/lod/util/DetailDistanceUtil.java | 6 +- 7 files changed, 108 insertions(+), 164 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java index 327140d12..1562bb544 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java @@ -1,13 +1,9 @@ package com.seibel.lod.builders.worldGeneration; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.*; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import com.seibel.lod.builders.GenerationRequest; import com.seibel.lod.builders.LodBuilder; @@ -20,11 +16,13 @@ import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LodThreadFactory; import com.seibel.lod.util.LodUtil; +import javafx.collections.transformation.SortedList; import net.minecraft.client.Minecraft; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.WorldWorkerManager; +import org.apache.commons.lang3.mutable.MutableBoolean; /** * A singleton that handles all long distance LOD world generation. @@ -69,6 +67,11 @@ public class LodWorldGenerator */ public static final LodWorldGenerator INSTANCE = new LodWorldGenerator(); + public volatile ConcurrentMap nodeToGenerate; + + SortedSet nodeToGenerateListNear; + SortedSet nodeToGenerateListFar; + private LodWorldGenerator() { @@ -107,72 +110,84 @@ public class LodWorldGenerator ArrayList chunksToGen = new ArrayList<>(maxChunkGenRequests); // if we don't have a full number of chunks to generate in chunksToGen // we can top it off from this reserve - ArrayList chunksToGenReserve = new ArrayList<>(maxChunkGenRequests); - - // how many level positions to - int requesting = maxChunkGenRequests; //=======================================// // create the generation Request objects // //=======================================// + List generationRequestList = new ArrayList<>(maxChunkGenRequests); - List farLevelPosListToGen; - List nearLevelPosListToGen; - List generationRequestList = new ArrayList<>(); + if (nodeToGenerate == null) + nodeToGenerate = new ConcurrentHashMap<>(); // start by generating half-region sized blocks... - int farRequesting = maxChunkGenRequests / 4; - byte maxDetailFar = (byte) 8; + //int farRequest = maxChunkGenRequests / 4; + //int nearRequest = maxChunkGenRequests * 3 /4; //we firstly make sure that the world is filled with half region wide block - farLevelPosListToGen = lodDim.getDataToGenerate( + Comparator posComparator = LevelPos.getPosComparator( playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailDistanceUtil.getDistanceGenerationMode(maxDetailFar).complexity, - maxDetailFar, - farRequesting); - farRequesting = farRequesting - farLevelPosListToGen.size(); - + playerBlockPosRounded.getZ()); + Comparator posLevelComparator = LevelPos.getPosAndDetailComparator( + playerBlockPosRounded.getX(), + playerBlockPosRounded.getZ()); + nodeToGenerateListNear = new TreeSet(posComparator); + nodeToGenerateListFar = new TreeSet(posLevelComparator); // ...then once the world is filled with big sized blocks // fill in the rest - int nearRequesting = maxChunkGenRequests - maxChunkGenRequests / 4 + farRequesting; + //int nearRequesting = maxChunkGenRequests - maxChunkGenRequests / 4 + farRequesting; //we then fill the world with the rest of the block - nearLevelPosListToGen = lodDim.getDataToGenerate( + lodDim.getDataToGenerate( + nodeToGenerate, playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailDistanceUtil.getDistanceGenerationMode(0).complexity, - DetailDistanceUtil.getLodDetail(0).detailLevel, - nearRequesting); + playerBlockPosRounded.getZ()); + + // how many level positions to + int requesting = maxChunkGenRequests; + + byte farDetail = (byte) 7; + //We alternate the generation between fast and near to make everything more smooth + for (LevelPos pos : nodeToGenerate.keySet()) + { + if (!nodeToGenerate.get(pos).booleanValue()) + { + nodeToGenerate.remove(pos); + } else + { + if (pos.detailLevel > farDetail){ + nodeToGenerateListFar.add(pos); + } + nodeToGenerateListNear.add(pos); + nodeToGenerate.get(pos).setFalse(); + } + } - byte minDetail; int maxDistance; byte circle; LevelPos levelPos; - //We alternate the generation between fast and near to make everything more smooth - while(!nearLevelPosListToGen.isEmpty() || !farLevelPosListToGen.isEmpty()){ - if(!nearLevelPosListToGen.isEmpty()) + int requestingFar = maxChunkGenRequests / 4; + while (requesting > 0 && !nodeToGenerateListNear.isEmpty()) + { + levelPos = nodeToGenerateListNear.first(); + nodeToGenerate.remove(levelPos); + nodeToGenerateListNear.remove(levelPos); + + maxDistance = levelPos.maxDistance( playerBlockPosRounded.getX(), playerBlockPosRounded.getZ()); + circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance); + generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(circle), DetailDistanceUtil.getLodDetail(circle))); + requesting--; + if (requestingFar > 0 && !nodeToGenerateListFar.isEmpty()) { - levelPos = nearLevelPosListToGen.get(0); - nearLevelPosListToGen.remove(0); - minDetail = (byte) 0; - maxDistance = levelPos.maxDistance( - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ()); - circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance, minDetail); - generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(circle), DetailDistanceUtil.getLodDetail(circle))); - } - if(!farLevelPosListToGen.isEmpty()) - { - levelPos = farLevelPosListToGen.get(0); - farLevelPosListToGen.remove(0); - minDetail = maxDetailFar; - maxDistance = levelPos.maxDistance( - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ()); - circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance, minDetail); - generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(circle), DetailDistanceUtil.getLodDetail(circle))); + levelPos = nodeToGenerateListFar.first(); + if (levelPos.detailLevel >= farDetail) + { + maxDistance = levelPos.maxDistance( playerBlockPosRounded.getX(), playerBlockPosRounded.getZ()); + circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance); + generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(circle), DetailDistanceUtil.getLodDetail(circle))); + requestingFar--; + requesting--; + } } } @@ -186,71 +201,18 @@ public class LodWorldGenerator for (GenerationRequest generationRequest : generationRequestList) { ChunkPos chunkPos = generationRequest.getChunkPos(); - if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) { // prevent generating the same chunk multiple times if (positionWaitingToBeGenerated.contains(chunkPos)) { - // ClientProxy.LOGGER.debug(pos + " asked to be generated again."); continue; } - - // determine if this position is closer to the player - // than the previous - int newDistance = playerChunkPos.getChessboardDistance(chunkPos); - - if (newDistance < minChunkDist) - { - // this chunk is closer, clear any previous - // positions and update the new minimum distance - minChunkDist = newDistance; - - // move all the old chunks into the reserve - ArrayList oldReserve = new ArrayList<>(chunksToGenReserve); - chunksToGenReserve.clear(); - chunksToGenReserve.addAll(chunksToGen); - // top off reserve with whatever was in oldReerve - for (int i = 0; i < oldReserve.size(); i++) - { - if (chunksToGenReserve.size() < maxChunkGenRequests) - chunksToGenReserve.add(oldReserve.get(i)); - else - break; - } - - chunksToGen.clear(); - chunksToGen.add(generationRequest); - } else if (newDistance == minChunkDist) - { - // this chunk position as close as the minimum distance - if (chunksToGen.size() < maxChunkGenRequests) - { - // we are still under the number of chunks to generate - // add this position to the list - chunksToGen.add(generationRequest); - } - } else - { - // this chunk is farther away than the minimum distance, - // add it to the reserve to make sure we always have a full reserve - chunksToGenReserve.add(generationRequest); - } + chunksToGen.add(generationRequest); } // lod null and can generate more chunks } // positions to generate - // fill up chunksToGen from the reserve if it isn't full - // already - if (chunksToGen.size() < maxChunkGenRequests) - { - Iterator reserveIterator = chunksToGenReserve.iterator(); - while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext()) - { - chunksToGen.add(reserveIterator.next()); - } - } - //=============================// // start the LodNodeGenWorkers // diff --git a/src/main/java/com/seibel/lod/handlers/LodConfig.java b/src/main/java/com/seibel/lod/handlers/LodConfig.java index 1c6702443..cfd378a7a 100644 --- a/src/main/java/com/seibel/lod/handlers/LodConfig.java +++ b/src/main/java/com/seibel/lod/handlers/LodConfig.java @@ -179,12 +179,12 @@ public class LodConfig .comment("\n\n" + " this value is multiplied by 128 and determine \n" + " how much the quality decrease over distance \n") - .defineInRange("lodQuality", 1, 2, 4); + .defineInRange("lodQuality", 1, 1, 4); lodChunkRenderDistance = builder .comment("\n\n" + " This is the render distance of the mod \n") - .defineInRange("lodChunkRenderDistane", 64, 32, 256); + .defineInRange("lodChunkRenderDistane", 64, 32, 512); distanceGenerationMode = builder .comment("\n\n" diff --git a/src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java b/src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java index e7fff0ab9..37ec14a21 100644 --- a/src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java +++ b/src/main/java/com/seibel/lod/objects/LevelPos/LevelPos.java @@ -11,7 +11,7 @@ public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos, public byte detailLevel; public int posX; public int posZ; - + public boolean flag; public LevelPos() { @@ -298,7 +298,7 @@ public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos, @Override public int compare(LevelPos first, LevelPos second) { - int compareResult = Integer.compare(first.detailLevel, second.detailLevel); + int compareResult = Integer.compare(second.detailLevel, first.detailLevel); if (compareResult == 0) { compareResult = Integer.compare( diff --git a/src/main/java/com/seibel/lod/objects/LodDimension.java b/src/main/java/com/seibel/lod/objects/LodDimension.java index bfe588cf0..60bbff055 100644 --- a/src/main/java/com/seibel/lod/objects/LodDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodDimension.java @@ -20,10 +20,7 @@ package com.seibel.lod.objects; import java.io.File; import java.io.IOException; import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -484,7 +481,7 @@ public class LodDimension * * @return list of quadTrees */ - public List getDataToGenerate(int playerPosX, int playerPosZ, byte generation, byte detailLevel, int dataNumber) + public void getDataToGenerate(ConcurrentMap dataToGenerate, int playerPosX, int playerPosZ) { int n = regions.length; @@ -492,7 +489,6 @@ public class LodDimension int zIndex; LodRegion region; RegionPos regionPos; - List listOfData = new ArrayList<>(); for (int xRegion = 0; xRegion < n; xRegion++) { for (int zRegion = 0; zRegion < n; zRegion++) @@ -503,7 +499,7 @@ public class LodDimension zIndex = (zRegion + center.z) - halfWidth; regionPos = new RegionPos(xIndex, zIndex); region = getRegion(regionPos); - listOfData.addAll(region.getDataToGenerate(playerPosX, playerPosZ, generation, detailLevel, dataNumber)); + region.getDataToGenerate(dataToGenerate, playerPosX, playerPosZ); } catch (Exception e) { @@ -511,17 +507,6 @@ public class LodDimension } } } - - List levelMinPosList = new ArrayList<>(); - dataNumber = Math.min(dataNumber, listOfData.size()); - - for (int i = 0; i < dataNumber; i++) - { - LevelPos min = Collections.min(listOfData, LevelPos.getPosComparator(playerPosX, playerPosZ)); - listOfData.remove(min); - levelMinPosList.add(min); - } - return levelMinPosList; } /** diff --git a/src/main/java/com/seibel/lod/objects/LodRegion.java b/src/main/java/com/seibel/lod/objects/LodRegion.java index 9b293eef4..17af2414b 100644 --- a/src/main/java/com/seibel/lod/objects/LodRegion.java +++ b/src/main/java/com/seibel/lod/objects/LodRegion.java @@ -172,33 +172,20 @@ public class LodRegion implements Serializable * * @return */ - public List getDataToGenerate(int playerPosX, int playerPosZ, byte generation, byte detailLevel, int dataNumber) + public void getDataToGenerate(ConcurrentMap dataToGenerate, int playerPosX, int playerPosZ) { LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0); - List levelPosList = new ArrayList<>(); - getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, generation, detailLevel); - List levelMinPosList = new ArrayList<>(); - dataNumber = Math.min(dataNumber, levelPosList.size()); - - LevelPos min; - for (int i = 0; i < dataNumber; i++) - { - min = Collections.min(levelPosList, LevelPos.getPosComparator(playerPosX, playerPosZ)); - levelPosList.remove(min); - levelMinPosList.add(min); - } - - return levelMinPosList; + getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ); } - private void getDataToGenerate(List levelPosList, LevelPos levelPos, int playerPosX, int playerPosZ, byte generation, byte targetDetailLevel) + private void getDataToGenerate(ConcurrentMap dataToGenerate, LevelPos levelPos, int playerPosX, int playerPosZ) { int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel); //here i calculate the the LevelPos is in range //This is important to avoid any kind of hole in the generation - int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); + int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ); int posX = levelPos.posX; @@ -210,15 +197,21 @@ public class LodRegion implements Serializable int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel + 1); //we have reached the target detail level - if (DetailDistanceUtil.getDistanceGenerationInverse(maxDistance,targetDetailLevel) > detailLevel) + if (DetailDistanceUtil.getDistanceGenerationInverse(minDistance) > detailLevel) { return; } - else if (DetailDistanceUtil.getDistanceGenerationInverse(maxDistance,targetDetailLevel) == levelPos.detailLevel) + else if (DetailDistanceUtil.getDistanceGenerationInverse(minDistance) == detailLevel) { if (!doesDataExist(levelPos)) { - levelPosList.add(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size)); + levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size); + if(dataToGenerate.containsKey(levelPos)){ + dataToGenerate.get(levelPos).setTrue(); + }else + { + dataToGenerate.put(levelPos.clone(), new MutableBoolean(true)); + } } } else { @@ -236,7 +229,13 @@ public class LodRegion implements Serializable if (!doesDataExist(levelPos)) { num++; - levelPosList.add(new LevelPos(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize)); + levelPos.changeParameters(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize); + if(dataToGenerate.containsKey(levelPos)){ + dataToGenerate.get(levelPos).setTrue(); + }else + { + dataToGenerate.put(levelPos.clone(), new MutableBoolean(true)); + } } } } @@ -249,25 +248,25 @@ public class LodRegion implements Serializable for (int z = 0; z <= 1; z++) { levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z); - getDataToGenerate(levelPosList, levelPos, - playerPosX, playerPosZ, generation, targetDetailLevel); + getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ); } } } } else //now we keep exploring the top right child { - levelPos.changeParameters(levelPos.detailLevel, levelPos.posX, levelPos.posZ); - levelPos.convert((byte) (levelPos.detailLevel - 1)); + levelPos.changeParameters((byte) (detailLevel-1), levelPos.posX*2 + regionPosX * childSize, levelPos.posZ*2 + regionPosZ * childSize); if (!doesDataExist(levelPos)) { - levelPosList.add(new LevelPos(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize)); + if(dataToGenerate.containsKey(levelPos)){ + dataToGenerate.get(levelPos).setTrue(); + }else + { + dataToGenerate.put(levelPos.clone(), new MutableBoolean(true)); + } } else { - if (levelPos.detailLevel != targetDetailLevel) - { - getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, generation, targetDetailLevel); - } + getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ); } } } diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index 3613693e6..5bc7ef2f8 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -217,7 +217,7 @@ public class LodRenderer // should LODs be regenerated? long newTime = System.currentTimeMillis(); //We check if the player has moved - if (newTime - prevPlayerPosTime > 2000) + if (newTime - prevPlayerPosTime > 5000) { if (previousPos.detailLevel == 0 || player.xChunk != previousPos.posX || @@ -233,7 +233,7 @@ public class LodRenderer prevPlayerPosTime = newTime; } //We check if the vanilla rendered chunks are changed - if (newTime - prevVanillaChunkTime > 1000) + if (newTime - prevVanillaChunkTime > 5000) { if (previousVanillaRenderedChunks.equals(vanillaRenderedChunks)) { diff --git a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java index a995399e0..c62b96150 100644 --- a/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java +++ b/src/main/java/com/seibel/lod/util/DetailDistanceUtil.java @@ -98,13 +98,11 @@ public class DetailDistanceUtil return (byte) Math.min(detail, LodUtil.REGION_DETAIL_LEVEL); } - public static byte getDistanceGenerationInverse(int distance, byte minDetailLevel) + public static byte getDistanceGenerationInverse(int distance) { - return (byte) Math.max(getDistanceRenderingInverse((int) (distance * genMultiplier)) - ,minDetailLevel); + return getDistanceRenderingInverse((int) (distance * genMultiplier)); } - public static byte getDistanceTreeCutInverse(int distance) { return getDistanceRenderingInverse((int) (distance * treeCutMultiplier));