Re-add biome blending

This commit is contained in:
James Seibel
2025-10-05 16:23:09 -05:00
parent aed5bb4163
commit 463565384b
8 changed files with 138 additions and 32 deletions
@@ -347,19 +347,17 @@ public class Config
.addListener(ReloadLodsConfigEventHandler.DELAYED_INSTANCE)
.build();
// TODO fixme
//public static ConfigEntry<Integer> lodBiomeBlending = new ConfigEntry.Builder<Integer>()
// .setMinDefaultMax(0,1,7)
// .comment(""
// + "This is the same as vanilla Biome Blending settings for Lod area. \n"
// + " Note that anything other than '0' will greatly effect Lod building time \n"
// + " and increase triangle count. The cost on chunk generation speed is also \n"
// + " quite large if set too high.\n"
// + "\n"
// + " '0' equals to Vanilla Biome Blending of '1x1' or 'OFF', \n"
// + " '1' equals to Vanilla Biome Blending of '3x3', \n"
// + " '2' equals to Vanilla Biome Blending of '5x5'...")
// .build();
public static ConfigEntry<Integer> lodBiomeBlending = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(0,3,3) // going higher than 3 causes banding issues for blending across LOD borders and an exponential increase in load times
.comment(""
+ "This is the same as vanilla Biome Blending settings for Lod area. \n"
+ " Note that anything other than '0' will greatly effect Lod building time. \n"
+ "\n"
+ " '0' equals to Vanilla Biome Blending of '1x1' or 'OFF', \n"
+ " '1' equals to Vanilla Biome Blending of '3x3', \n"
+ " '2' equals to Vanilla Biome Blending of '5x5'...")
.addListener(ReloadLodsConfigEventHandler.DELAYED_INSTANCE)
.build();
}
public static class Ssao
@@ -114,6 +114,15 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, false);
this.put(EDhApiQualityPreset.EXTREME, false);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, Integer> biomeBlending = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.lodBiomeBlending,
new HashMap<EDhApiQualityPreset, Integer>()
{{
this.put(EDhApiQualityPreset.MINIMUM, 0);
this.put(EDhApiQualityPreset.LOW, 1);
this.put(EDhApiQualityPreset.MEDIUM, 3);
this.put(EDhApiQualityPreset.HIGH, 3);
this.put(EDhApiQualityPreset.EXTREME, 3);
}});
@@ -133,6 +142,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.configList.add(this.vanillaFade);
this.configList.add(this.dhDither);
this.configList.add(this.caveCulling);
this.configList.add(this.biomeBlending);
for (ConfigEntryWithPresetOptions<EDhApiQualityPreset, ?> config : this.configList)
@@ -116,7 +116,7 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
public String getKeyDisplayString() { return DhSectionPos.toString(this.pos); }
@Override
public Long getPos() { return this.pos; }
public long getPos() { return this.pos; }
public void resizeDataStructuresForRepopulation(long pos)
{
@@ -32,7 +32,9 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pooling.AbstractPhantomArrayList;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListCheckout;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.util.*;
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -281,10 +283,83 @@ public class FullDataSourceV2
//======//
// data //
// getters //
//======//
public LongArrayList get(int relX, int relZ) throws IndexOutOfBoundsException { return this.dataPoints[relativePosToIndex(relX, relZ)]; }
public LongArrayList get(int relX, int relZ) throws IndexOutOfBoundsException
{ return this.dataPoints[relativePosToIndex(relX, relZ)]; }
/**
* returns {@link FullDataPointUtil#EMPTY_DATA_POINT} if the given {@link DhBlockPos}
* is outside this data source's boundaries.
*/
public long getAtBlockPos(DhBlockPos blockPos)
{
DhLodPos requestedPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.getX(), blockPos.getZ());
// stop if the requested blockPos is outside this datasource
{
// get the detail levels for this request
byte requestedDetailLevel = requestedPos.detailLevel;
byte requestedSectionDetailLevel = (byte) (requestedDetailLevel + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
// get the positions for this request
long sectionPos = requestedPos.getSectionPosWithSectionDetailLevel(requestedSectionDetailLevel);
if (!DhSectionPos.contains(this.pos, sectionPos))
{
return FullDataPointUtil.EMPTY_DATA_POINT;
}
}
// get the relative data source position
byte requestDetailLevel = (byte) (DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
DhLodPos relativePos = requestedPos.getDhSectionRelativePositionForDetailLevel(requestDetailLevel);
// get the data column
LongArrayList dataColumn = this.get(relativePos.x, relativePos.z);
if (dataColumn == null)
{
return FullDataPointUtil.EMPTY_DATA_POINT;
}
// search for a datapoint that contains the given block y position
long dataPoint;
for (int i = 0; i < dataColumn.size(); i++)
{
dataPoint = dataColumn.getLong(i);
// we are looking for a specific datapoint,
// don't look at null ones
if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT)
{
continue;
}
int requestedY = blockPos.getY();
int bottomY = FullDataPointUtil.getBottomY(dataPoint) + this.levelMinY;
int height = FullDataPointUtil.getHeight(dataPoint);
int topY = bottomY + height;
// does this datapoint contain the requested Y position?
if (bottomY <= requestedY
&& requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY
{
return dataPoint;
}
}
return FullDataPointUtil.EMPTY_DATA_POINT;
}
//==========//
// updating //
//==========//
@Override
public boolean update(@NotNull FullDataSourceV2 inputDataSource, @Nullable IDhLevel level) { return this.update(inputDataSource); }
@@ -991,6 +1066,7 @@ public class FullDataSourceV2
}
//================//
// helper methods //
//================//
@@ -1075,7 +1151,7 @@ public class FullDataSourceV2
//=====================//
@Override
public Long getPos() { return this.pos; }
public long getPos() { return this.pos; }
@Override
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); }
@@ -131,7 +131,7 @@ public class FullDataToRenderDataTransformer
LongArrayList dataColumn = fullDataSource.get(x, z);
updateOrReplaceRenderDataViewColumnWithFullDataColumn(
levelWrapper, fullDataSource.mapping,
levelWrapper, fullDataSource,
// bitshift is to account for LODs with a detail level greater than 0 so the block pos is correct
baseX + BitShiftUtil.pow(x,dataDetail), baseZ + BitShiftUtil.pow(z,dataDetail),
columnArrayView, dataColumn);
@@ -145,13 +145,14 @@ public class FullDataToRenderDataTransformer
/** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */
public static void updateOrReplaceRenderDataViewColumnWithFullDataColumn(
IClientLevelWrapper levelWrapper,
FullDataPointIdMap fullDataMapping, int blockX, int blockZ,
IClientLevelWrapper levelWrapper,
FullDataSourceV2 fullDataSource, int blockX, int blockZ,
ColumnArrayView columnArrayView,
LongArrayList fullDataColumn)
{
// we can't do anything if the full data is missing or empty
if (fullDataColumn == null || fullDataColumn.size() == 0)
if (fullDataColumn == null
|| fullDataColumn.size() == 0)
{
return;
}
@@ -160,7 +161,7 @@ public class FullDataToRenderDataTransformer
if (fullDataLength <= columnArrayView.verticalSize())
{
// Directly use the arrayView since it fits.
setRenderColumnView(levelWrapper, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn);
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn);
}
else
{
@@ -171,7 +172,7 @@ public class FullDataToRenderDataTransformer
{
// expand the ColumnArrayView to fit the new larger max vertical size
ColumnArrayView newColumnArrayView = new ColumnArrayView(dataArrayList, fullDataLength, 0, fullDataLength);
setRenderColumnView(levelWrapper, fullDataMapping, blockX, blockZ, newColumnArrayView, fullDataColumn);
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, newColumnArrayView, fullDataColumn);
columnArrayView.changeVerticalSizeFrom(newColumnArrayView);
}
finally
@@ -181,7 +182,7 @@ public class FullDataToRenderDataTransformer
}
}
private static void setRenderColumnView(
IClientLevelWrapper levelWrapper, FullDataPointIdMap fullDataMapping,
IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource,
int blockX, int blockZ,
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
{
@@ -222,6 +223,8 @@ public class FullDataToRenderDataTransformer
// convert full data to render data //
//==================================//
FullDataPointIdMap fullDataMapping = fullDataSource.mapping;
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockX, 0, blockZ);
// goes from the top down
@@ -320,11 +323,13 @@ public class FullDataToRenderDataTransformer
//=======================//
if (ignoreNonCollidingBlocks
&& !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
&& !block.isSolid()
&& !block.isLiquid()
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
{
if (colorBelowWithAvoidedBlocks)
{
int tempColor = levelWrapper.getBlockColor(mutableBlockPos, biome, block);
int tempColor = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
// don't transfer the color when alpha is 0
// this prevents issues if grass is transparent
@@ -345,7 +350,7 @@ public class FullDataToRenderDataTransformer
if (colorToApplyToNextBlock == -1)
{
// use this block's color
color = levelWrapper.getBlockColor(mutableBlockPos, biome, block);
color = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
}
else
{
@@ -14,7 +14,7 @@ import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
*/
public interface IDataSource<TDhLevel extends IDhLevel> extends IBaseDTO<Long>, AutoCloseable
{
Long getPos();
long getPos();
/** @return true if the data was changed */
boolean update(FullDataSourceV2 chunkData, TDhLevel level);
@@ -221,11 +221,27 @@ public class DhSectionPos
byte offset = (byte) (detailLevel - returnDetailLevel);
return BitShiftUtil.powerOfTwo(offset);
}
/** @return how wide this section is in blocks */
public static int getBlockWidth(long pos) { return BitShiftUtil.powerOfTwo(getDetailLevel(pos)); }
/** @return how wide this section is in chunks */
public static int getChunkWidth(long pos) { return DhSectionPos.getBlockWidth(pos) / LodUtil.CHUNK_WIDTH; }
/** @see DhSectionPos#getDetailLevelWidthInBlocks(byte) */
public static int getBlockWidth(long pos) { return getDetailLevelWidthInBlocks(getDetailLevel(pos)); }
/**
* Returns how many blocks wide a single LOD at the given detail level would be in blocks. <br>
* IE: <br>
* <code>
* 0 => 1 <br>
* 1 => 2 <br>
* 2 => 4 <br>
* 3 => 8 <br>
* 4 => 16 <br>
* 5 => 32 <br>
* 6 => 64 <br>
* etc. <br>
* </code>
*/
public static int getDetailLevelWidthInBlocks(byte detailLevel) { return BitShiftUtil.powerOfTwo(detailLevel); }
public static DhBlockPos2D getCenterBlockPos(long pos) { return new DhBlockPos2D(getCenterBlockPosX(pos), getCenterBlockPosZ(pos)); }
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.world;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import org.jetbrains.annotations.Nullable;
@@ -34,7 +35,7 @@ public interface IClientLevelWrapper extends ILevelWrapper
@Nullable
IServerLevelWrapper tryGetServerSideWrapper();
int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState);
int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockState);
/** @return -1 if there was a problem getting the color */
int getDirtBlockColor();
/** @return -1 if there was a problem getting the color */