Add Cave Culling setting + cleanup + fix leo's adjData

This commit is contained in:
tom lee
2022-03-20 16:26:50 +08:00
parent 8f534fb51c
commit 21253d1308
6 changed files with 112 additions and 70 deletions
@@ -286,6 +286,28 @@ public class LodRegion {
priority, genMode, shouldSort, needFarPos);
}
}
public byte getRenderDetailLevelAt(int playerPosX, int playerPosZ, byte detailLevel, int offsetX, int offsetZ) {
GenerationPriority generationPriority = CONFIG.client().worldGenerator().getResolvedGenerationPriority();
DropoffQuality dropoffQuality = CONFIG.client().graphics().quality().getResolvedDropoffQuality();
double minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ,
playerPosX, playerPosZ);
byte targetLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
byte renderLevel;
if (targetLevel > dropoffQuality.fastModeSwitch) {
double centerDistance = LevelPosUtil.centerDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX, playerPosZ);
renderLevel = DetailDistanceUtil.getDetailLevelFromDistance(centerDistance);
} else {
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
double posMinDistance = LevelPosUtil.minDistance(detailLevel,
LevelPosUtil.getRegionModule(detailLevel, offsetX) + regionPosX*size,
LevelPosUtil.getRegionModule(detailLevel, offsetZ) + regionPosZ*size,
playerPosX, playerPosZ);
renderLevel = DetailDistanceUtil.getDetailLevelFromDistance(posMinDistance);
}
return (byte) Math.max(getMinDetailLevel(), renderLevel);
}
public void getPosToRender(PosToRenderContainer posToRender, int playerPosX,
int playerPosZ)
@@ -314,9 +336,10 @@ public class LodRegion {
priority);
} else {
// FarModeSwitchLevel or above is the level where a giant block of lod is not acceptable even if not all child data exist.
double maxDistance = LevelPosUtil.maxDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX, playerPosZ);
double centerDistance = LevelPosUtil.centerDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX, playerPosZ);
targetLevel = DetailDistanceUtil.getDetailLevelFromDistance(centerDistance);
byte farModeSwitchLevel = (priority == GenerationPriority.NEAR_FIRST) ? 0 :
calculateFarModeSwitch(DetailDistanceUtil.getDetailLevelFromDistance(maxDistance));
calculateFarModeSwitch(targetLevel);
if (priority == GenerationPriority.FAR_FIRST) farModeSwitchLevel = 8;
getPosToRenderFlat(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, targetLevel, farModeSwitchLevel);
}
@@ -10,8 +10,10 @@ import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.LodDirection.Axis;
import com.seibel.lod.core.enums.config.GpuUploadMethod;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
public class LodQuadBuilder {
static final int MAX_BUFFER_SIZE = (1024 * 1024 * 1);
@@ -19,7 +21,9 @@ public class LodQuadBuilder {
static final int MAX_QUADS_PER_BUFFER = MAX_BUFFER_SIZE / QUAD_BYTE_SIZE;
//static final int MAX_MERGED_QUAD_SIZE = 64;
public boolean skipSkylight0Quads = true;
static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
public final boolean skipSkylight0Quads;
static class Quad {
final short x;
@@ -225,9 +229,10 @@ public class LodQuadBuilder {
final ArrayList<Quad>[] quads;
public LodQuadBuilder(int initialSize) {
public LodQuadBuilder(int initialSize, boolean enableSkylightCulling) {
quads = new ArrayList[6];
for (int i=0; i<6; i++) quads[i] = new ArrayList<Quad>();
this.skipSkylight0Quads = enableSkylightCulling;
}
public void addQuadAdj(LodDirection dir, short x, short y, short z, short w0, short wy, int color, byte skylight,
@@ -366,6 +371,7 @@ public class LodQuadBuilder {
public void mergeQuads() {
if(true)return;
long mergeCount = 0;
long preQuadsCount = getCurrentQuadsCount();
if (preQuadsCount<=1) return;
@@ -170,7 +170,10 @@ public class RenderRegion implements AutoCloseable
return CompletableFuture.supplyAsync(() -> {
try {
if (ENABLE_EVENT_STEP_LOGGING) ApiShared.LOGGER.info("RenderRegion start QuadBuild @ {}", regionPos);
LodQuadBuilder builder = new LodQuadBuilder(10);
boolean useSkylightCulling = CONFIG.client().graphics().advancedGraphics().getEnableCaveCulling();
useSkylightCulling &= !lodDim.dimension.hasCeiling();
useSkylightCulling &= lodDim.dimension.hasSkyLight();
LodQuadBuilder builder = new LodQuadBuilder(10, useSkylightCulling);
Runnable buildRun = ()->{
makeLodRenderData(builder, region, adjRegions, playerPosX, playerPosZ);
};
@@ -325,64 +328,53 @@ public class RenderRegion implements AutoCloseable
try {
int xAdj = posX + lodDirection.getNormal().x;
int zAdj = posZ + lodDirection.getNormal().z;
byte adjDetail = detailLevel;
int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj);
int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj);
Boolean isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
boolean adjSkip = isRenderedAdj!=null && isRenderedAdj;
//We check if the adjPos is to be rendered
boolean renderAdjPos = posToRender.contains(detailLevel, xAdj, zAdj);
boolean doesAdjLowerPosExist = detailLevel==0 ? false : posToRender.contains((byte) (detailLevel-1), xAdj*2, zAdj*2);
boolean renderLowerAdjPos = doesAdjLowerPosExist;
LodRegion adjRegion = region;
//since he system doesn't work for region border we need to check with another system
if(!renderAdjPos && (!doesAdjLowerPosExist || detailLevel==0))
{
//we compute the distance from the adjPos
double minDistance = LevelPosUtil.minDistance(detailLevel, xAdj, zAdj, playerX, playerZ) - 1.4142*(2 << detailLevel);
if (isRenderedAdj!=null && isRenderedAdj) continue;
boolean isCrossRegionBoundary = LevelPosUtil.getRegion(detailLevel, xAdj) != region.regionPosX ||
LevelPosUtil.getRegion(detailLevel, zAdj) != region.regionPosZ;
LodRegion adjRegion;
byte adjDetail;
int childXAdj = xAdj*2 + (lodDirection.getNormal().x<0 ? 1 : 0);
int childZAdj = zAdj*2 + (lodDirection.getNormal().z<0 ? 1 : 0);
//we check if the detail of the adjPos is equal to the correct one (region border fix)
//or if the detail is wrong by 1 value (region+circle border fix)
if (isCrossRegionBoundary) {
//we compute at which detail that position should be rendered
adjRegion = adjRegions[lodDirection.ordinal()-2];
byte minLevel;
if(adjRegion != null)
{
minLevel = (byte) Math.max(adjRegion.getMinDetailLevel(),
DetailDistanceUtil.getDetailLevelFromDistance(minDistance));
} else{
minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
}
//we check if the detail of the adjPos is equal to the correct one (region border fix)
//or if the detail is wrong by 1 value (region+circle border fix)
renderAdjPos = detailLevel == minLevel;
renderLowerAdjPos = detailLevel==0 ? false : detailLevel-1 == minLevel;
if(adjRegion == null) continue;
adjDetail = adjRegion.getRenderDetailLevelAt(playerX, playerZ, detailLevel, xAdj, zAdj);
} else {
adjRegion = region;
if (posToRender.contains(detailLevel, xAdj, zAdj)) adjDetail = detailLevel;
else if (detailLevel>0 &&
posToRender.contains((byte) (detailLevel-1), childXAdj, childZAdj))
adjDetail = (byte) (detailLevel-1);
else if (detailLevel<LodUtil.REGION_DETAIL_LEVEL &&
posToRender.contains((byte) (detailLevel+1), xAdj/2, zAdj/2))
adjDetail = (byte) (detailLevel+1);
else continue;
}
if (adjRegion == null) continue;
if (renderAdjPos && !adjSkip) {
//The adj data is at same detail and is extracted
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
} else if (renderLowerAdjPos)
{
//The adj data is at lower detail and is extracted in two steps
xAdj *= 2;
zAdj *= 2;
adjDetail = (byte) (detailLevel - 1);
if (adjDetail < detailLevel-1 || adjDetail > detailLevel+1) {
continue;
}
if (adjDetail == detailLevel || adjDetail > detailLevel) {
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail,
LevelPosUtil.convert(detailLevel, xAdj, adjDetail),
LevelPosUtil.convert(detailLevel, zAdj, adjDetail));
} else {
adjData[lodDirection.ordinal() - 2] = new long[2][];
isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
adjSkip = isRenderedAdj!=null && isRenderedAdj;
if (!adjSkip) {
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
}
xAdj += Math.abs(lodDirection.getNormal().x);
zAdj += Math.abs(lodDirection.getNormal().z);
isRenderedAdj = chunkGrid.get(chunkXAdj, chunkZAdj);
adjSkip = isRenderedAdj!=null && isRenderedAdj;
if (!adjSkip)
{
adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail, xAdj, zAdj);
}
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getAllData(adjDetail,
childXAdj, childZAdj);
adjData[lodDirection.ordinal() - 2][1] = adjRegion.getAllData(adjDetail,
childXAdj + (lodDirection.getAxis()==LodDirection.Axis.X ? 0 : 1),
childZAdj + (lodDirection.getAxis()==LodDirection.Axis.Z ? 0 : 1));
}
} catch (RuntimeException e) {
ApiShared.LOGGER.warn("Failed to get adj data for [{}:{},{}] at [{}]", detailLevel, posX, posZ, lodDirection);
@@ -148,22 +148,26 @@ public class LevelPosUtil
{
return convert(detailLevel, pos, LodUtil.CHUNK_DETAIL_LEVEL);
}
public static double myPow2(double x)
{
return x*x;
public static double centerDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ) {
int width = 1 << detailLevel;
double cPosX = posX * width + width/2.;
double cPosZ = posZ * width + width/2.;
cPosX = playerPosX - cPosX;
cPosZ = playerPosZ - cPosZ;
return Math.sqrt(LodUtil.pow2(cPosX) + LodUtil.pow2(cPosZ));
}
public static double maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
{
int width = 1 << detailLevel;
double startPosX = posX * width;
double startPosZ = posZ * width;
double endPosX = myPow2(playerPosX - startPosX - width);
double endPosZ = myPow2(playerPosZ - startPosZ - width);
startPosX = myPow2(playerPosX - startPosX);
startPosZ = myPow2(playerPosZ - startPosZ);
double endPosX = LodUtil.pow2(playerPosX - startPosX - width);
double endPosZ = LodUtil.pow2(playerPosZ - startPosZ - width);
startPosX = LodUtil.pow2(playerPosX - startPosX);
startPosZ = LodUtil.pow2(playerPosZ - startPosZ);
double maxDistance = Math.sqrt(startPosX + startPosZ);
maxDistance = Math.max(maxDistance, Math.sqrt(startPosX + endPosZ));
@@ -203,10 +207,10 @@ public class LevelPosUtil
}
else
{
double startPosX2 = myPow2(playerPosX - startPosX);
double startPosZ2 = myPow2(playerPosZ - startPosZ);
double endPosX2 = myPow2(playerPosX - endPosX);
double endPosZ2 = myPow2(playerPosZ - endPosZ);
double startPosX2 = LodUtil.pow2(playerPosX - startPosX);
double startPosZ2 = LodUtil.pow2(playerPosZ - startPosZ);
double endPosX2 = LodUtil.pow2(playerPosX - endPosX);
double endPosZ2 = LodUtil.pow2(playerPosZ - endPosZ);
double minDistance = Math.sqrt(startPosX2 + startPosZ2);
minDistance = Math.min(minDistance, Math.sqrt(startPosX2 + endPosZ2));
@@ -523,7 +523,20 @@ public interface ILodConfigWrapperSingleton extends IBindable
+ " 2 = very saturated \n";
double getSaturationMultiplier();
void setSaturationMultiplier(double newSaturationMultiplier);
boolean ENABLE_CAVE_CULLING_DEFAULT = false;
String ENABLE_CAVE_CULLING_DESC = ""
+ " If enabled caves will be culled \n"
+ "\n"
+ " NOTE: This feature is under development and \n"
+ " it is VERY experimental! Please don't report \n"
+ " any issues related to this feature. \n"
+ "\n"
+ " Additional Info: Currently this cull all faces \n"
+ " with skylight value of 0 in dimensions that \n"
+ " does not have a ceiling. \n";
boolean getEnableCaveCulling();
void setEnableCaveCulling(boolean newEnableCaveCulling);
}
}
@@ -160,6 +160,10 @@
"Saturation Multiplier",
"DistantHorizons.config.client.graphics.advancedGraphics.saturationMultiplier.@tooltip":
"How saturated fake chunk colors are.\n\n0 = black and white \n1 = normal \n2 = vibrant",
"DistantHorizons.config.client.graphics.advancedGraphics.enableCaveCulling":
"Cave Culling §6(EXPERIMENTAL)§r",
"DistantHorizons.config.client.graphics.advancedGraphics.enableCaveCulling.@tooltip":
"If enabled caves will be culled \n\n§6NOTE: This feature is under development and \n it is VERY experimental! Please don't report \nany issues related to this feature.§r \n\nAdditional Info: Currently this cull all faces \n with skylight value of 0 in dimensions that \n does not have a ceiling. \n",
"DistantHorizons.config.client.worldGenerator":
"World generator",
"DistantHorizons.config.client.worldGenerator.generationPriority":