Improve LOD detail level detection and hole filling
This commit is contained in:
+44
-11
@@ -19,8 +19,10 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
@@ -61,11 +63,11 @@ public class ColumnBox
|
||||
//=========//
|
||||
|
||||
public static void addBoxQuadsToBuilder(
|
||||
LodQuadBuilder builder,
|
||||
LodQuadBuilder builder, IDhClientLevel clientLevel,
|
||||
short xSize, short ySize, short zSize,
|
||||
short x, short minY, short z,
|
||||
int color, byte irisBlockMaterialId, byte skyLight, byte blockLight,
|
||||
long topData, long bottomData, ColumnArrayView[] adjData)
|
||||
long topData, long bottomData, ColumnArrayView[] adjData, boolean[] isAdjDataSameDetailLevel)
|
||||
{
|
||||
//================//
|
||||
// variable setup //
|
||||
@@ -82,6 +84,15 @@ public class ColumnBox
|
||||
boolean isTopTransparent = RenderDataPointUtil.getAlpha(topData) < 255 && LodRenderer.transparencyEnabled;
|
||||
boolean isBottomTransparent = RenderDataPointUtil.getAlpha(bottomData) < 255 && LodRenderer.transparencyEnabled;
|
||||
|
||||
// defaulting to a value far below what we can normally render means we
|
||||
// don't need to have an additional "is cave culling enabled" check
|
||||
int caveCullingMaxY = Integer.MIN_VALUE;
|
||||
if (Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get())
|
||||
{
|
||||
caveCullingMaxY = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get() - clientLevel.getMinY();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if there isn't any data below this LOD, make this LOD's color opaque to prevent seeing void through transparent blocks
|
||||
// Note: this LOD should still be considered transparent for this method's checks, otherwise rendering bugs may occur
|
||||
@@ -135,12 +146,11 @@ public class ColumnBox
|
||||
// NORTH face
|
||||
{
|
||||
ColumnArrayView adjCol = adjData[EDhDirection.NORTH.ordinal() - 2]; // TODO can we use something other than ordinal-2?
|
||||
// if the adjacent column is null that generally means it's representing a different detail level
|
||||
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.NORTH.ordinal() - 2];
|
||||
// if the adjacent column is null that generally means the adjacent area hasn't been generated yet
|
||||
if (adjCol == null)
|
||||
{
|
||||
// Add an adjacent face if this is opaque face or transparent over the void.
|
||||
// By skipping transparent faces that aren't over the void we prevent adding ocean faces
|
||||
// between detail levels.
|
||||
if (!isTransparent || overVoid)
|
||||
{
|
||||
builder.addQuadAdj(EDhDirection.NORTH, x, minY, z, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||
@@ -148,7 +158,7 @@ public class ColumnBox
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjVerticalQuad(builder, adjCol, EDhDirection.NORTH, x, minY, z, xSize, ySize,
|
||||
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH, x, minY, z, xSize, ySize,
|
||||
color, irisBlockMaterialId, blockLight);
|
||||
}
|
||||
}
|
||||
@@ -156,6 +166,7 @@ public class ColumnBox
|
||||
// SOUTH face
|
||||
{
|
||||
ColumnArrayView adjCol = adjData[EDhDirection.SOUTH.ordinal() - 2];
|
||||
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.SOUTH.ordinal() - 2];
|
||||
if (adjCol == null)
|
||||
{
|
||||
if (!isTransparent || overVoid)
|
||||
@@ -165,7 +176,7 @@ public class ColumnBox
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjVerticalQuad(builder, adjCol, EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize,
|
||||
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize,
|
||||
color, irisBlockMaterialId, blockLight);
|
||||
}
|
||||
}
|
||||
@@ -173,6 +184,7 @@ public class ColumnBox
|
||||
// WEST face
|
||||
{
|
||||
ColumnArrayView adjCol = adjData[EDhDirection.WEST.ordinal() - 2];
|
||||
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.WEST.ordinal() - 2];
|
||||
if (adjCol == null)
|
||||
{
|
||||
if (!isTransparent || overVoid)
|
||||
@@ -182,7 +194,7 @@ public class ColumnBox
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjVerticalQuad(builder, adjCol, EDhDirection.WEST, x, minY, z, zSize, ySize,
|
||||
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST, x, minY, z, zSize, ySize,
|
||||
color, irisBlockMaterialId, blockLight);
|
||||
}
|
||||
}
|
||||
@@ -190,6 +202,7 @@ public class ColumnBox
|
||||
// EAST face
|
||||
{
|
||||
ColumnArrayView adjCol = adjData[EDhDirection.EAST.ordinal() - 2];
|
||||
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.EAST.ordinal() - 2];
|
||||
if (adjCol == null)
|
||||
{
|
||||
if (!isTransparent || overVoid)
|
||||
@@ -199,14 +212,14 @@ public class ColumnBox
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjVerticalQuad(builder, adjCol, EDhDirection.EAST, maxX, minY, z, zSize, ySize,
|
||||
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST, maxX, minY, z, zSize, ySize,
|
||||
color, irisBlockMaterialId, blockLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void makeAdjVerticalQuad(
|
||||
LodQuadBuilder builder, @NotNull ColumnArrayView adjColumnView, EDhDirection direction,
|
||||
LodQuadBuilder builder, @NotNull ColumnArrayView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
||||
short x, short yMin, short z, short horizontalWidth, short ySize,
|
||||
int color, byte irisBlockMaterialId, byte blockLight)
|
||||
{
|
||||
@@ -283,10 +296,30 @@ public class ColumnBox
|
||||
{
|
||||
// adj opaque
|
||||
// mark positions adjacent is covering
|
||||
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
|
||||
for (int i = adjMinY; i < adjMaxY; i++)
|
||||
{
|
||||
byte skyLightAtPos = skyLightAtInputPos[i];
|
||||
skyLightAtInputPos[i] = (byte) Math.min(SKYLIGHT_COVERED, skyLightAtPos);
|
||||
|
||||
// if the adjacent is a different detail level, we want to render adjacent opaque
|
||||
// faces to try and reduce the chance of holes on detail level borders
|
||||
boolean adjacentCoversThis =
|
||||
// if the adjacent is the same detail level, no special handling is necessary
|
||||
!adjacentIsSameDetailLevel
|
||||
// if the adjacent face is underground we probably don't need it
|
||||
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
|
||||
// check if this face is on a border
|
||||
&&
|
||||
(
|
||||
(x == 0 && direction == EDhDirection.WEST)
|
||||
|| (z == 0 && direction == EDhDirection.NORTH)
|
||||
// TODO why does 256 represent a border? aren't LODs only 64 datapoints wide?
|
||||
|| (x == 256 && direction == EDhDirection.EAST)
|
||||
|| (z == 256 && direction == EDhDirection.SOUTH)
|
||||
);
|
||||
|
||||
byte newSkyLightAtPos = adjacentCoversThis ? adjSkyLight : SKYLIGHT_COVERED;
|
||||
skyLightAtInputPos[i] = (byte) Math.min(newSkyLightAtPos, skyLightAtPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+17
-13
@@ -63,7 +63,7 @@ public class ColumnRenderBufferBuilder
|
||||
|
||||
public static CompletableFuture<LodQuadBuilder> buildBuffersAsync(
|
||||
IDhClientLevel clientLevel,
|
||||
ColumnRenderSource renderSource, ColumnRenderSource[] adjData
|
||||
ColumnRenderSource renderSource, ColumnRenderSource[] adjData, boolean[] isSameDetailLevel
|
||||
)
|
||||
{
|
||||
ThreadPoolExecutor bufferBuilderExecutor = ThreadPoolUtil.getBufferBuilderExecutor();
|
||||
@@ -84,7 +84,7 @@ public class ColumnRenderBufferBuilder
|
||||
{
|
||||
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
||||
LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper());
|
||||
makeLodRenderData(builder, renderSource, adjData);
|
||||
makeLodRenderData(builder, renderSource, clientLevel, adjData, isSameDetailLevel);
|
||||
return builder;
|
||||
}
|
||||
catch (UncheckedInterruptedException e)
|
||||
@@ -172,7 +172,9 @@ public class ColumnRenderBufferBuilder
|
||||
return future;
|
||||
}
|
||||
}
|
||||
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, ColumnRenderSource[] adjRegions)
|
||||
private static void makeLodRenderData(
|
||||
LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel,
|
||||
ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel)
|
||||
{
|
||||
//=============//
|
||||
// debug check //
|
||||
@@ -362,8 +364,9 @@ public class ColumnRenderBufferBuilder
|
||||
long bottomDataPoint = (i + 1) < columnRenderData.size() ? columnRenderData.get(i + 1) : RenderDataPointUtil.EMPTY_DATA;
|
||||
|
||||
addLodToBuffer(
|
||||
clientLevel,
|
||||
data, topDataPoint, bottomDataPoint,
|
||||
adjColumnViews,
|
||||
adjColumnViews, isSameDetailLevel,
|
||||
thisDetailLevel, relX, relZ,
|
||||
quadBuilder, debugSourceFlag);
|
||||
}
|
||||
@@ -374,8 +377,9 @@ public class ColumnRenderBufferBuilder
|
||||
quadBuilder.finalizeData();
|
||||
}
|
||||
private static void addLodToBuffer(
|
||||
IDhClientLevel clientLevel,
|
||||
long data, long topData, long bottomData,
|
||||
ColumnArrayView[] adjColumnViews,
|
||||
ColumnArrayView[] adjColumnViews, boolean[] isSameDetailLevel,
|
||||
byte detailLevel, int renderSourceOffsetPosX, int renderSourceOffsetPosZ,
|
||||
LodQuadBuilder quadBuilder, ColumnRenderSource.DebugSourceFlag debugSource)
|
||||
{
|
||||
@@ -505,14 +509,14 @@ public class ColumnRenderBufferBuilder
|
||||
}
|
||||
|
||||
ColumnBox.addBoxQuadsToBuilder(
|
||||
quadBuilder, // buffer
|
||||
width, ySize, width, // setWidth
|
||||
x, yMin, z, // setOffset
|
||||
color, // setColor
|
||||
blockMaterialId, // irisBlockMaterialId
|
||||
RenderDataPointUtil.getLightSky(data), // setSkyLights
|
||||
fullBright ? 15 : RenderDataPointUtil.getLightBlock(data), // setBlockLights
|
||||
topData, bottomData, adjColumnViews); // setAdjData
|
||||
quadBuilder, clientLevel,
|
||||
width, ySize, width,
|
||||
x, yMin, z,
|
||||
color,
|
||||
blockMaterialId,
|
||||
RenderDataPointUtil.getLightSky(data),
|
||||
fullBright ? 15 : RenderDataPointUtil.getLightBlock(data),
|
||||
topData, bottomData, adjColumnViews, isSameDetailLevel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -202,9 +202,15 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
ColumnRenderBuffer previousBuffer = this.renderBuffer;
|
||||
|
||||
ColumnRenderSource[] adjacentRenderSections = new ColumnRenderSource[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
boolean[] adjIsSameDetailLevel = new boolean[EDhDirection.ADJ_DIRECTIONS.length];
|
||||
for (int i = 0; i < EDhDirection.ADJ_DIRECTIONS.length; i++)
|
||||
{
|
||||
adjacentRenderSections[i] = adjRenderSourceLoadRefFutures[i].future.getNow(null);
|
||||
|
||||
// if the adjacent position isn't the same detail level the buffer building logic
|
||||
// will need to be slightly different in order to reduce holes in the LODs
|
||||
EDhDirection direction = EDhDirection.ADJ_DIRECTIONS[i];
|
||||
adjIsSameDetailLevel[direction.ordinal() - 2] = this.isAdjacentPosSameDetailLevel(direction);
|
||||
}
|
||||
|
||||
if (this.bufferBuildFuture != null)
|
||||
@@ -213,7 +219,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
// prevents the CPU from working on something that won't be used
|
||||
this.bufferBuildFuture.cancel(true);
|
||||
}
|
||||
this.bufferBuildFuture = ColumnRenderBufferBuilder.buildBuffersAsync(this.level, renderSource, adjacentRenderSections);
|
||||
this.bufferBuildFuture = ColumnRenderBufferBuilder.buildBuffersAsync(this.level, renderSource, adjacentRenderSections, adjIsSameDetailLevel);
|
||||
this.bufferBuildFuture.thenAccept((lodQuadBuilder) ->
|
||||
{
|
||||
|
||||
@@ -283,17 +289,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
long adjPos = DhSectionPos.getAdjacentPos(this.pos, direction);
|
||||
try
|
||||
{
|
||||
// ignore adjacent positions that aren't the same detail level
|
||||
// since the LodDataBuilder can't handle different detail levels
|
||||
byte detailLevel = this.quadTree.calculateExpectedDetailLevel(new DhBlockPos2D(MC.getPlayerBlockPos()), adjPos);
|
||||
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
if (detailLevel == DhSectionPos.getDetailLevel(this.pos))
|
||||
LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos);
|
||||
if (adjRenderSection != null)
|
||||
{
|
||||
LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos);
|
||||
if (adjRenderSection != null)
|
||||
{
|
||||
futureArray[arrayIndex] = adjRenderSection.getRenderSourceAsync();
|
||||
}
|
||||
futureArray[arrayIndex] = adjRenderSection.getRenderSourceAsync();
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignore) {}
|
||||
@@ -354,6 +353,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
this.getRenderSourceLock.unlock();
|
||||
}
|
||||
}
|
||||
private boolean isAdjacentPosSameDetailLevel(EDhDirection direction)
|
||||
{
|
||||
long adjPos = DhSectionPos.getAdjacentPos(this.pos, direction);
|
||||
byte detailLevel = this.quadTree.calculateExpectedDetailLevel(new DhBlockPos2D(MC.getPlayerBlockPos()), adjPos);
|
||||
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
boolean adjacentIsSameDetailLevel = (detailLevel == DhSectionPos.getDetailLevel(this.pos));
|
||||
return adjacentIsSameDetailLevel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user