changed the generation. Added re-use of levelPos

This commit is contained in:
Leonardo
2021-08-30 23:03:22 +02:00
parent cdba7b20f5
commit 193c579712
7 changed files with 108 additions and 164 deletions
@@ -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<LevelPos, MutableBoolean> nodeToGenerate;
SortedSet<LevelPos> nodeToGenerateListNear;
SortedSet<LevelPos> nodeToGenerateListFar;
private LodWorldGenerator()
{
@@ -107,72 +110,84 @@ public class LodWorldGenerator
ArrayList<GenerationRequest> 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<GenerationRequest> chunksToGenReserve = new ArrayList<>(maxChunkGenRequests);
// how many level positions to
int requesting = maxChunkGenRequests;
//=======================================//
// create the generation Request objects //
//=======================================//
List<GenerationRequest> generationRequestList = new ArrayList<>(maxChunkGenRequests);
List<LevelPos> farLevelPosListToGen;
List<LevelPos> nearLevelPosListToGen;
List<GenerationRequest> 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<LevelPos> posComparator = LevelPos.getPosComparator(
playerBlockPosRounded.getX(),
playerBlockPosRounded.getZ(),
DetailDistanceUtil.getDistanceGenerationMode(maxDetailFar).complexity,
maxDetailFar,
farRequesting);
farRequesting = farRequesting - farLevelPosListToGen.size();
playerBlockPosRounded.getZ());
Comparator<LevelPos> 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<GenerationRequest> 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<GenerationRequest> reserveIterator = chunksToGenReserve.iterator();
while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext())
{
chunksToGen.add(reserveIterator.next());
}
}
//=============================//
// start the LodNodeGenWorkers //
@@ -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"
@@ -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(
@@ -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<LevelPos> getDataToGenerate(int playerPosX, int playerPosZ, byte generation, byte detailLevel, int dataNumber)
public void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, int playerPosX, int playerPosZ)
{
int n = regions.length;
@@ -492,7 +489,6 @@ public class LodDimension
int zIndex;
LodRegion region;
RegionPos regionPos;
List<LevelPos> 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<LevelPos> 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;
}
/**
@@ -172,33 +172,20 @@ public class LodRegion implements Serializable
*
* @return
*/
public List<LevelPos> getDataToGenerate(int playerPosX, int playerPosZ, byte generation, byte detailLevel, int dataNumber)
public void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, int playerPosX, int playerPosZ)
{
LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0);
List<LevelPos> levelPosList = new ArrayList<>();
getDataToGenerate(levelPosList, levelPos, playerPosX, playerPosZ, generation, detailLevel);
List<LevelPos> 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<LevelPos> levelPosList, LevelPos levelPos, int playerPosX, int playerPosZ, byte generation, byte targetDetailLevel)
private void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> 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);
}
}
}
@@ -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))
{
@@ -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));