This commit is contained in:
s809
2024-07-28 19:01:28 +05:00
27 changed files with 783 additions and 1111 deletions
@@ -24,7 +24,7 @@ package com.seibel.distanthorizons.api.enums;
* CHUNK - Detail Level: 4, width 16 block, <br>
* REGION - Detail Level: 9, width 512 block <br> <br>
*
* Detail levels in Distant Horizons represent how large a section (of either LODs or MC chunks)
* Detail levels in Distant Horizons represent how large a LOD
* is, with the smallest being 0 (1 block wide). <br>
* The width of a detail level can be calculated by putting the detail level to the power of 2. <br>
* Example for the chunk detail level (4): 2^4 = 16 blocks wide <Br><br>
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.api.enums.rendering;
* AIR, <br>
* ILLUMINATED, <br>
*
* @author IMS
* @author James Seibel
* @since API 3.0.0
* @version 2024-7-11
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.api.interfaces.block;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
/**
@@ -44,7 +45,11 @@ public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper
* @since API 3.0.0
*/
String getSerialString();
/** @since API 3.0.0 */
/**
* Returns the byte value representing the {@link EDhApiBlockMaterial} enum.
* @see EDhApiBlockMaterial
* @since API 3.0.0
*/
byte getMaterialId();
}
@@ -26,7 +26,7 @@ import com.seibel.distanthorizons.api.objects.DhApiResult;
* Used to interact with Distant Horizons' rendering system.
*
* @author James Seibel
* @version 2023-10-13
* @version 2024-7-27
* @since API 1.0.0
*/
public interface IDhApiRenderProxy
@@ -39,10 +39,8 @@ public interface IDhApiRenderProxy
* If this is called on a dedicated server it won't do anything and will return {@link DhApiResult#success} = false <Br><Br>
*
* Background: <Br>
* Distant Horizons has two different file formats: Full data and Render data. <Br>
* - Full data files store the block, biome, etc. information and is the result of loading or generating new chunks. <Br>
* - Render data files store LOD colors and are created using the Full data and currently loaded resource packs. <Br>
* This is the data cleared by this method.
* When rendering Distant Horizons bakes each block's color into the geometry that's rendered. <Br>
* This improves rendering speed and VRAM size, but prevents dynamically changing LOD colors. <Br>
*/
DhApiResult<Boolean> clearRenderDataCache();
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
* A level is equivalent to a dimension in vanilla Minecraft.
*
* @author James Seibel
* @version 2022-7-14
* @version 2024-7-28
* @since API 1.0.0
*/
public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
@@ -43,7 +43,18 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
boolean hasSkyLight();
/** Returns the max block height of the level(?) */
/**
* Deprecated, use {@link IDhApiLevelWrapper#getMaxHeight} instead. <br>
* Returns the max block height of the level.
*
* @see IDhApiLevelWrapper#getMaxHeight
*/
@Deprecated
default int getHeight() { return this.getMaxHeight(); }
/**
* Returns the max block height of the level
* @since API 3.0.0
*/
int getMaxHeight();
/**
@@ -55,6 +66,8 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
/**
* Will return null if called on the server,
* or if called before the renderer has been set up.
*
* @since API 3.0.0
*/
IDhApiCustomRenderRegister getRenderRegister();
@@ -52,7 +52,27 @@ public class DhApiChunk
// constructors //
//==============//
public DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
/**
* Deprecated due to the topYBlockPos and bottomYBlockPos variables being put in the wrong order.
* They should have been in bottom -> top order.
*
* @see DhApiChunk#create(int, int, int, int)
*/
@Deprecated
public DhApiChunk(int chunkPosX, int chunkPosZ, int topYBlockPos, int bottomYBlockPos)
{ this(chunkPosX, chunkPosZ, bottomYBlockPos, topYBlockPos, false); }
/**
* @since API 3.0.0
*/
public static DhApiChunk create(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
{ return new DhApiChunk(chunkPosX, chunkPosZ, topYBlockPos, bottomYBlockPos, false); }
/**
* Only visible to internal DH methods
* @param ignoredParameter is only present to differentiate the two constructors and isn't actually used
*/
private DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos, boolean ignoredParameter)
{
this.chunkPosX = chunkPosX;
this.chunkPosZ = chunkPosZ;
@@ -19,9 +19,12 @@
package com.seibel.distanthorizons.api.objects.data;
import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import java.util.ArrayList;
/**
* Holds a single datapoint of terrain data.
*
@@ -37,6 +40,8 @@ public class DhApiTerrainDataPoint
* 2 = 4x4 blocks <br>
* 4 = chunk (16x16 blocks) <br>
* 9 = region (512x512 blocks) <br>
*
* @see EDhApiDetailLevel
*/
public final byte detailLevel;
@@ -50,7 +55,57 @@ public class DhApiTerrainDataPoint
public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int bottomYBlockPos, int topYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
//==============//
// constructors //
//==============//
/**
* Deprecated due to the topYBlockPos and bottomYBlockPos variables being put in the wrong order.
* They should have been in bottom -> top order.
*
* @see DhApiTerrainDataPoint#create(byte, int, int, int, int, IDhApiBlockStateWrapper, IDhApiBiomeWrapper)
*/
@Deprecated
public DhApiTerrainDataPoint(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int topYBlockPos, int bottomYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
{
this(detailLevel, blockLightLevel, skyLightLevel,
bottomYBlockPos, topYBlockPos,
blockStateWrapper, biomeWrapper,
false);
}
/**
* @since API 3.0.0
*/
public static DhApiTerrainDataPoint create(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int bottomYBlockPos, int topYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper
)
{
return new DhApiTerrainDataPoint(
detailLevel, blockLightLevel, skyLightLevel,
bottomYBlockPos, topYBlockPos,
blockStateWrapper, biomeWrapper,
false);
}
/**
* Only visible to internal DH methods
* @param ignoredParameter is only present to differentiate the two constructors and isn't actually used
*/
private DhApiTerrainDataPoint(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int bottomYBlockPos, int topYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper,
boolean ignoredParameter
)
{
this.detailLevel = detailLevel;
@@ -326,9 +326,10 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
int height = FullDataPointUtil.getHeight(dataPoint);
int topY = bottomY + height;
return new DhApiTerrainDataPoint(detailLevel,
return DhApiTerrainDataPoint.create(
detailLevel,
FullDataPointUtil.getBlockLight(dataPoint), FullDataPointUtil.getSkyLight(dataPoint),
topY, bottomY,
bottomY, topY,
blockState, biomeWrapper);
}
@@ -1461,6 +1461,22 @@ public class Config
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugXRow = new ConfigEntry.Builder<Integer>()
.set(-1)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugZRow = new ConfigEntry.Builder<Integer>()
.set(-1)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugColumnIndex = new ConfigEntry.Builder<Integer>()
.set(-1)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.build();
public static ConfigCategory debugWireframe = new ConfigCategory.Builder()
@@ -182,7 +182,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
EDhApiWorldGenerationStep worldGenStep = inputFullDataSource.getWorldGenStepAtRelativePos(x, z);
if (dataColumn != null && worldGenStep != EDhApiWorldGenerationStep.EMPTY)
{
FullDataToRenderDataTransformer.updateRenderDataViewWithFullDataColumn(
FullDataToRenderDataTransformer.updateOrReplaceRenderDataViewColumnWithFullDataColumn(
level, inputFullDataSource.mapping,
minBlockPos.x + x,
minBlockPos.z + z,
@@ -288,7 +288,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
String SUBDATA_DELIMITER = ",";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(this.pos);
stringBuilder.append(DhSectionPos.toString(this.pos));
stringBuilder.append(LINE_DELIMITER);
int size = 1;
@@ -19,29 +19,58 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class ColumnBox
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
/**
* if the skylight has this value that means
* no data is expected
*/
private static final byte SKYLIGHT_EMPTY = -1;
/**
* if the skylight has this value that means
* that block position is covered/occuled by an adjacent block/column.
*/
private static final byte SKYLIGHT_COVERED = -2;
private static final ThreadLocal<byte[]> THREAD_LOCAL_SKY_LIGHT_ARRAY = ThreadLocal.withInitial(() ->
{
byte[] array = new byte[RenderDataPointUtil.MAX_WORLD_Y_SIZE];
Arrays.fill(array, SKYLIGHT_EMPTY);
return array;
});
//=========//
// builder //
//=========//
public static void addBoxQuadsToBuilder(
LodQuadBuilder builder,
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)
{
//================//
// variable setup //
//================//
short maxX = (short) (x + xSize);
short maxY = (short) (minY + ySize);
short maxZ = (short) (z + zSize);
@@ -53,33 +82,15 @@ public class ColumnBox
boolean isTopTransparent = RenderDataPointUtil.getAlpha(topData) < 255 && LodRenderer.transparencyEnabled;
boolean isBottomTransparent = RenderDataPointUtil.getAlpha(bottomData) < 255 && LodRenderer.transparencyEnabled;
// 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
// FIXME this transparency change should be applied before this point since this could affect other areas
// This may also be better than handling the LOD as transparent, but that is TBD
if (!RenderDataPointUtil.doesDataPointExist(bottomData))
{
color = ColorUtil.setAlpha(color, 255);
}
// cave culling prevention
// prevents certain faces from being culled underground that should be allowed
if (builder.skipQuadsWithZeroSkylight
&& 0 == skyLight
&& builder.skyLightCullingBelow > maxY
&& (
(RenderDataPointUtil.getAlpha(topData) < 255 && RenderDataPointUtil.getYMax(topData) >= builder.skyLightCullingBelow)
|| (RenderDataPointUtil.getYMin(topData) >= builder.skyLightCullingBelow)
|| !RenderDataPointUtil.doesDataPointExist(topData)
)
)
{
maxY = builder.skyLightCullingBelow;
}
// fake ocean transparency
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
{
@@ -99,7 +110,9 @@ public class ColumnBox
// add top and bottom faces if requested //
//==========================//
// add top and bottom faces //
//==========================//
boolean skipTop = RenderDataPointUtil.doesDataPointExist(topData) && (RenderDataPointUtil.getYMin(topData) == maxY) && !isTopTransparent;
if (!skipTop)
@@ -114,409 +127,269 @@ public class ColumnBox
}
// add North, south, east, and west faces if requested //
// TODO merge duplicate code
//NORTH face vertex creation
//========================================//
// add North, south, east, and west faces //
//========================================//
// NORTH face
{
ColumnArrayView[] adjDataNorth = adjData[EDhDirection.NORTH.ordinal() - 2]; // TODO can we use something other than ordinal-2?
int adjOverlapNorth = ColorUtil.INVISIBLE;
if (adjDataNorth == null)
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
if (adjCol == null)
{
// add an adjacent face if this is opaque face or transparent over the void
// 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);
}
}
else if (adjDataNorth.length == 1)
{
makeAdjVerticalQuad(builder, adjDataNorth[0], EDhDirection.NORTH, x, minY, z, xSize, ySize,
color, adjOverlapNorth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
else
{
makeAdjVerticalQuad(builder, adjDataNorth[0], EDhDirection.NORTH, x, minY, z, (short) (xSize / 2), ySize,
color, adjOverlapNorth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataNorth[1], EDhDirection.NORTH, (short) (x + xSize / 2), minY, z, (short) (xSize / 2), ySize,
color, adjOverlapNorth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, EDhDirection.NORTH, x, minY, z, xSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
//SOUTH face vertex creation
// SOUTH face
{
ColumnArrayView[] adjDataSouth = adjData[EDhDirection.SOUTH.ordinal() - 2];
int adjOverlapSouth = ColorUtil.INVISIBLE;
if (adjDataSouth == null)
ColumnArrayView adjCol = adjData[EDhDirection.SOUTH.ordinal() - 2];
if (adjCol == null)
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
else if (adjDataSouth.length == 1)
{
makeAdjVerticalQuad(builder, adjDataSouth[0], EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize,
color, adjOverlapSouth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
}
else
{
makeAdjVerticalQuad(builder, adjDataSouth[0], EDhDirection.SOUTH, x, minY, maxZ, (short) (xSize / 2), ySize,
color, adjOverlapSouth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataSouth[1], EDhDirection.SOUTH, (short) (x + xSize / 2), minY, maxZ, (short) (xSize / 2), ySize,
color, adjOverlapSouth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
//WEST face vertex creation
// WEST face
{
ColumnArrayView[] adjDataWest = adjData[EDhDirection.WEST.ordinal() - 2];
int adjOverlapWest = ColorUtil.INVISIBLE;
if (adjDataWest == null)
ColumnArrayView adjCol = adjData[EDhDirection.WEST.ordinal() - 2];
if (adjCol == null)
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.WEST, x, minY, z, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
else if (adjDataWest.length == 1)
{
makeAdjVerticalQuad(builder, adjDataWest[0], EDhDirection.WEST, x, minY, z, zSize, ySize,
color, adjOverlapWest, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
}
else
{
makeAdjVerticalQuad(builder, adjDataWest[0], EDhDirection.WEST, x, minY, z, (short) (zSize / 2), ySize,
color, adjOverlapWest, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataWest[1], EDhDirection.WEST, x, minY, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
color, adjOverlapWest, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, EDhDirection.WEST, x, minY, z, zSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
//EAST face vertex creation
// EAST face
{
ColumnArrayView[] adjDataEast = adjData[EDhDirection.EAST.ordinal() - 2];
int adjOverlapEast = ColorUtil.INVISIBLE;
if (adjData[EDhDirection.EAST.ordinal() - 2] == null)
ColumnArrayView adjCol = adjData[EDhDirection.EAST.ordinal() - 2];
if (adjCol == null)
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.EAST, maxX, minY, z, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
else if (adjDataEast.length == 1)
{
makeAdjVerticalQuad(builder, adjDataEast[0], EDhDirection.EAST, maxX, minY, z, zSize, ySize,
color, adjOverlapEast, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
}
else
{
makeAdjVerticalQuad(builder, adjDataEast[0], EDhDirection.EAST, maxX, minY, z, (short) (zSize / 2), ySize,
color, adjOverlapEast, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataEast[1], EDhDirection.EAST, maxX, minY, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
color, adjOverlapEast, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, EDhDirection.EAST, maxX, minY, z, zSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
}
// the overlap color can be used to see faces that shouldn't be rendered
private static void makeAdjVerticalQuad(
LodQuadBuilder builder, ColumnArrayView adjColumnView, EDhDirection direction,
LodQuadBuilder builder, @NotNull ColumnArrayView adjColumnView, EDhDirection direction,
short x, short yMin, short z, short horizontalWidth, short ySize,
int color, int debugOverlapColor, byte irisBlockMaterialId, byte skyLightTop, byte blockLight,
long topData, long bottomData)
int color, byte irisBlockMaterialId, byte blockLight)
{
//==================//
// create face with //
// no adjacent data //
//==================//
color = ColorUtil.applyShade(color, MC.getShade(direction));
if (adjColumnView == null || adjColumnView.size == 0 || RenderDataPointUtil.isVoid(adjColumnView.get(0)))
// if there isn't any data adjacent to this LOD,
// just add the full vertical quad
if (adjColumnView.size == 0 || RenderDataPointUtil.isVoid(adjColumnView.get(0)))
{
// there isn't any data adjacent to this LOD, add the vertical quad
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
return;
}
int yMax = yMin + ySize;
int adjIndex;
boolean firstFace = true;
boolean inputAboveAdjLods = true;
short previousAdjDepth = -1;
byte nextTopSkyLight = skyLightTop;
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
boolean lastAdjWasTransparent = false;
//===========================//
// Determine face visibility //
// based on it's neighbors //
//===========================//
short yMax = (short) (yMin + ySize); // min is inclusive, max is exclusive
byte[] skyLightAtInputPos = THREAD_LOCAL_SKY_LIGHT_ARRAY.get();
if (!RenderDataPointUtil.doesDataPointExist(bottomData))
try
{
// there isn't anything under this LOD,
// to prevent seeing through the world, make it opaque
color = ColorUtil.setAlpha(color, 255);
}
// Add adjacent faces if this LOD is surrounded by transparent LODs
// (prevents invisible sides underwater)
int adjCount = adjColumnView.size();
for (adjIndex = 0; // iterates top down
adjIndex < adjCount
&& RenderDataPointUtil.doesDataPointExist(adjColumnView.get(adjIndex))
&& !RenderDataPointUtil.isVoid(adjColumnView.get(adjIndex));
adjIndex++)
{
long adjPoint = adjColumnView.get(adjIndex);
// set the initial sky-lights for this face,
// if nothing overlaps or overhangs the face should have max sky light
Arrays.fill(skyLightAtInputPos, yMin, yMax, LodUtil.MAX_MC_LIGHT);
// if the adjacent data point is over the void
// don't consider it as transparent
// FIXME this transparency change should be applied before this point since this could affect other areas
boolean adjOverVoid = false;
if (adjIndex > 0)
// iterate top down
int adjCount = adjColumnView.size();
for (int adjIndex = 0; adjIndex < adjCount; adjIndex++)
{
long adjBellowPoint = adjColumnView.get(adjIndex-1);
adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBellowPoint);
}
boolean adjTransparent = !adjOverVoid && RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
// continue if this data point is transparent or the adjacent point is not
if (inputTransparent || !adjTransparent) // TODO inputIsTransparent may be unnecessary
{
short adjYMin = RenderDataPointUtil.getYMin(adjPoint);
short adjYMax = RenderDataPointUtil.getYMax(adjPoint);
long adjPoint = adjColumnView.get(adjIndex);
short adjMinY = RenderDataPointUtil.getYMin(adjPoint);
short adjMaxY = RenderDataPointUtil.getYMax(adjPoint);
// if fake transparency is enabled, allow for 1 block of transparency,
// everything under that should be opaque
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
// skip empty adjacent datapoints
if (!RenderDataPointUtil.doesDataPointExist(adjPoint)
|| RenderDataPointUtil.isVoid(adjPoint))
{
if (lastAdjWasTransparent && !adjTransparent)
{
adjYMax = (short) (RenderDataPointUtil.getYMax(adjColumnView.get(adjIndex - 1)) - 1);
}
else if (adjTransparent && (adjIndex + 1) < adjCount)
{
if (RenderDataPointUtil.getAlpha(adjColumnView.get(adjIndex + 1)) == 255)
{
adjYMin = (short) (adjYMax - 1);
}
}
}
if (yMax <= adjYMin)
{
// the adjacent LOD is above the input LOD and won't affect its rendering,
// skip to the next adjacent
continue;
}
inputAboveAdjLods = false;
if (adjYMax < yMin)
// skip this adjacent datapoint if it's above the input datapoint (since it can't affect the input data point)
if (yMax <= adjMinY)
{
// the adjacent LOD is below the input LOD
// getting the skylight is more complicated
// since LODs can be adjacent to water, which changes how skylight works
byte skyLight;
if (adjIndex == 0)
{
// this adj LOD is at the highest position,
// its sky lighting won't be affected by anything above it
skyLight = RenderDataPointUtil.getLightSky(adjPoint);
}
else
{
// TODO improve the comments here, this is a bit confusing
long aboveAdjPoint = adjColumnView.get(adjIndex - 1);
if (RenderDataPointUtil.getAlpha(aboveAdjPoint) != 255)
{
// above adjacent LOD is transparent...
boolean inputMaxHigherThanAboveAdj = yMax > RenderDataPointUtil.getYMax(aboveAdjPoint);
if (inputMaxHigherThanAboveAdj)
{
// ...and higher than the input yMax,
// use its sky light
skyLight = RenderDataPointUtil.getLightSky(aboveAdjPoint);
}
else
{
// ...and at or below the input yMax,
skyLight = RenderDataPointUtil.getLightSky(adjPoint);
}
}
else
{
// LOD above adjacent is opaque, use the adj LOD's skylight
skyLight = RenderDataPointUtil.getLightSky(adjPoint);
}
}
if (firstFace)
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, skyLight, blockLight);
}
else
{
// Now: adjMaxHeight < y < previousAdjDepth < yMax
if (previousAdjDepth == -1)
{
// TODO why is this an error?
throw new RuntimeException("Loop error");
}
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (previousAdjDepth - yMin), color, irisBlockMaterialId, skyLight, blockLight);
previousAdjDepth = -1;
}
// TODO why break here?
break;
continue;
}
if (adjYMin <= yMin)
long adjAbovePoint = (adjIndex != 0) ? adjColumnView.get(adjIndex - 1) : RenderDataPointUtil.EMPTY_DATA;
long adjBelowPoint = (adjIndex + 1 < adjCount) ? adjColumnView.get(adjIndex + 1) : RenderDataPointUtil.EMPTY_DATA;
// if the adjacent data point is over the void
// don't consider it as transparent
boolean adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBelowPoint);
boolean adjTransparent = !adjOverVoid && RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
//=================================//
// set sky light based on adjacent //
//=================================//
// set light based on overlapping adjacent
if (!adjTransparent)
{
// the adjacent LOD's base is at or below the input's base
if (yMax <= adjYMax)
// adj opaque
// mark positions adjacent is covering
for (int i = adjMinY; i < adjMaxY; i++)
{
// The input face is completely inside the adj's face, don't render it
if (debugOverlapColor != 0)
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(SKYLIGHT_COVERED, skyLightAtPos);
}
else
{
// the adj data intersects the lower part of the input data, don't render below the intersection
if (adjYMax > yMin && debugOverlapColor != 0)
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (adjYMax - yMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
// if this is the only face, use the yMax and break,
// if there was another face finish the last one and then break
if (firstFace)
{
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (yMax - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
else
{
// Now: depth <= y <= height <= previousAdjDepth < yMax
if (previousAdjDepth == -1)
{
// TODO why is this an error?
throw new RuntimeException("Loop error");
}
if (previousAdjDepth > adjYMax)
{
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
previousAdjDepth = -1;
}
}
// we don't need to check any other adjacent LODs
// since this one completely covers the input
break;
}
// In here always true: y < adjYMin < yMax
// _________________&&: y < ________ (height and yMax)
if (adjYMax >= yMax)
{
// Basically: y _______ < yMax <= height
// _______&&: y < depth < yMax
// the adj data intersects the higher part of the current data
if (debugOverlapColor != 0)
{
builder.addQuadAdj(direction, x, adjYMin, z, horizontalWidth, (short) (yMax - adjYMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
// we start the creation of a new face
}
else
{
// Otherwise: y < _____ height < yMax
// _______&&: y < depth ______ < yMax
if (debugOverlapColor != 0)
// adjacent is transparent,
// use datapoint below adjacent for lighting
byte belowSkyLight = RenderDataPointUtil.getLightSky(adjBelowPoint);
for (int i = adjMinY; i < adjMaxY; i++)
{
builder.addQuadAdj(direction, x, adjYMin, z, horizontalWidth, (short) (adjYMax - adjYMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
if (firstFace)
{
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (yMax - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
else
{
// Now: y < depth < height <= previousAdjDepth < yMax
if (previousAdjDepth == -1)
throw new RuntimeException("Loop error");
if (previousAdjDepth > adjYMax)
{
if (irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index)
{
// this LOD is underneath another, grass will never show here
irisBlockMaterialId = EDhApiBlockMaterial.DIRT.index;
}
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
previousAdjDepth = -1;
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(belowSkyLight, skyLightAtPos);
}
}
// set next top as current depth
previousAdjDepth = adjYMin;
firstFace = false;
nextTopSkyLight = skyLightTop;
if (adjIndex + 1 < adjColumnView.size() && RenderDataPointUtil.doesDataPointExist(adjColumnView.get(adjIndex + 1)))
// fill in sky light up to the next DP,
// this is done to handle overhangs
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
int adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
for (int i = adjMaxY; i < adjAboveMinY; i++)
{
nextTopSkyLight = RenderDataPointUtil.getLightSky(adjColumnView.get(adjIndex + 1));
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(adjSkyLight, skyLightAtPos);
}
}
//=======================//
// create vertical faces //
//=======================//
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
byte lastSkyLight = skyLightAtInputPos[yMin];
int quadBottomY = yMin;
int quadTopY = -1;
// walk up the sky lights and create a new face
// whenever the light changes to different valid value
for (int i = yMin; i < yMax; i++)
{
byte skyLight = skyLightAtInputPos[i];
if (skyLight != lastSkyLight)
{
// the sky light changed, create the in-progress face
tryAddVerticalFaceWithSkyLightToBuilder(
builder, direction,
x, z, horizontalWidth,
color, irisBlockMaterialId, blockLight,
lastSkyLight, inputTransparent, quadTopY, quadBottomY
);
lastSkyLight = skyLight;
quadBottomY = i;
}
lastAdjWasTransparent = adjTransparent;
quadTopY = (i + 1);
}
// add the in-progress face if present
if (quadTopY != -1)
{
tryAddVerticalFaceWithSkyLightToBuilder(
builder, direction,
x, z, horizontalWidth,
color, irisBlockMaterialId, blockLight,
lastSkyLight, inputTransparent, quadTopY, quadBottomY
);
}
}
if (inputAboveAdjLods)
finally
{
// the input LOD is above all adjacent LODs and won't be affected
// by them, add the vertical quad using the input's lighting and height
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, skyLightTop, blockLight);
// clean up the array before the next thread uses it
// (may be unnecessary since we only work between the yMin-yMax anyway, but is helpful for debugging)
Arrays.fill(skyLightAtInputPos, yMin, yMax, SKYLIGHT_EMPTY);
}
else if (previousAdjDepth != -1)
}
private static void tryAddVerticalFaceWithSkyLightToBuilder(
LodQuadBuilder builder, EDhDirection direction,
short x, short z, short horizontalWidth,
int color, byte irisBlockMaterialId, byte blockLight,
byte lastSkyLight, boolean inputTransparent, int quadTopY, int quadBottomY
)
{
// invalid positions will have a negative skylight
if (lastSkyLight >= 0)
{
// We need to finish the last quad.
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (previousAdjDepth - yMin), color, irisBlockMaterialId, nextTopSkyLight, blockLight);
// Don't add transparent vertical faces
// unless the adjacent position is empty.
// This is done to prevent walls between water blocks in the ocean.
if (!inputTransparent
|| (lastSkyLight == LodUtil.MAX_MC_LIGHT))
{
// don't add negative/empty height faces
short height = (short) (quadTopY - quadBottomY);
if (height > 0)
{
builder.addQuadAdj(direction, x, (short) quadBottomY, z, horizontalWidth, height, color, irisBlockMaterialId, lastSkyLight, blockLight);
}
}
}
}
}
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -30,7 +31,6 @@ import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.objects.StatsMap;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.core.util.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import org.apache.logging.log4j.Logger;
@@ -91,7 +91,7 @@ public class ColumnRenderBuffer implements AutoCloseable
/** Should be run on a DH thread. */
public void uploadBuffer(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException
{
LodUtil.assertTrue(Thread.currentThread().getName().startsWith(ThreadUtil.THREAD_NAME_PREFIX), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads.");
LodUtil.assertTrue(DhApi.isDhThread(), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads.");
// upload on MC's render thread
@@ -146,7 +146,7 @@ public class ColumnRenderBuffer implements AutoCloseable
{
// opaque vbos //
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
this.vbos = resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
for (int i = 0; i < this.vbos.length; i++)
{
if (this.vbos[i] == null)
@@ -163,7 +163,7 @@ public class ColumnRenderBuffer implements AutoCloseable
// transparent vbos //
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
this.vbosTransparent = resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
for (int i = 0; i < this.vbosTransparent.length; i++)
{
if (this.vbosTransparent[i] == null)
@@ -180,10 +180,10 @@ public class ColumnRenderBuffer implements AutoCloseable
private void uploadBuffersDirect(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException
{
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
this.vbos = resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
uploadBuffersDirect(this.vbos, builder.makeOpaqueVertexBuffers(), method);
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
this.vbosTransparent = resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
uploadBuffersDirect(this.vbosTransparent, builder.makeTransparentVertexBuffers(), method);
}
private static void uploadBuffersDirect(GLVertexBuffer[] vbos, Iterator<ByteBuffer> iter, EDhApiGpuUploadMethod method) throws InterruptedException
@@ -318,9 +318,9 @@ public class ColumnRenderBuffer implements AutoCloseable
//==============//
// misc methods //
//==============//
//================//
// helper methods //
//================//
/** can be used when debugging */
public boolean hasNonNullVbos() { return this.vbos != null || this.vbosTransparent != null; }
@@ -366,6 +366,35 @@ public class ColumnRenderBuffer implements AutoCloseable
}
}
public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize)
{
if (vbos.length == newSize)
{
return vbos;
}
GLVertexBuffer[] newVbos = new GLVertexBuffer[newSize];
System.arraycopy(vbos, 0, newVbos, 0, Math.min(vbos.length, newSize));
if (newSize < vbos.length)
{
for (int i = newSize; i < vbos.length; i++)
{
if (vbos[i] != null)
{
vbos[i].close();
}
}
}
return newVbos;
}
//================//
// base overrides //
//================//
/**
* This method is called when object is no longer in use.
* Called either after uploadBuffers() returned false (On buffer Upload
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.config.Config;
@@ -29,12 +30,13 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -59,6 +61,7 @@ public class ColumnRenderBufferBuilder
// vbo building //
//==============//
/** @link adjData should be null for adjacent sections that cross detail level boundaries */
public static CompletableFuture<ColumnRenderBuffer> buildAndUploadBuffersAsync(
IDhClientLevel clientLevel,
ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
@@ -81,16 +84,8 @@ public class ColumnRenderBufferBuilder
try
{
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
long builderStartTime = System.currentTimeMillis();
LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper());
makeLodRenderData(builder, renderSource, adjData);
long builderEndTime = System.currentTimeMillis();
long buildMs = builderEndTime - builderStartTime;
//LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs);
return builder;
}
catch (UncheckedInterruptedException e)
@@ -99,7 +94,7 @@ public class ColumnRenderBufferBuilder
}
catch (Throwable e3)
{
LOGGER.error("\"LodNodeBufferBuilder\" was unable to build quads: ", e3);
LOGGER.error("LodNodeBufferBuilder was unable to build quads for pos ["+DhSectionPos.toString(renderSource.pos)+"], error: ["+ e3.getMessage()+"].", e3);
throw e3;
}
}, bufferBuilderExecutor)
@@ -111,8 +106,15 @@ public class ColumnRenderBufferBuilder
try
{
buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod());
LodUtil.assertTrue(buffer.buffersUploaded);
return buffer;
if (buffer.buffersUploaded)
{
return buffer;
}
else
{
buffer.close();
return null;
}
}
catch (Exception e)
{
@@ -142,19 +144,20 @@ public class ColumnRenderBufferBuilder
}
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, ColumnRenderSource[] adjRegions)
{
// Variable initialization
EDhApiDebugRendering debugMode = Config.Client.Advanced.Debugging.debugRendering.get();
//=============//
// debug check //
//=============//
// can be used to limit which section positions are build and thus, rendered
// useful when debugging a specific section
boolean enableColumnBufferLimit = Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get();
if (enableColumnBufferLimit)
boolean columnBuilderDebugEnabled = Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get();
if (columnBuilderDebugEnabled)
{
if (DhSectionPos.getDetailLevel(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugDetailLevel.get()
&& DhSectionPos.getX(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugXPos.get()
&& DhSectionPos.getZ(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugZPos.get())
{
int test = 0;
int breakpoint = 0;
}
else
{
@@ -162,24 +165,22 @@ public class ColumnRenderBufferBuilder
}
}
byte detailLevel = renderSource.getDataDetailLevel();
for (int x = 0; x < ColumnRenderSource.SECTION_SIZE; x++)
//===================//
// build each column //
//===================//
byte thisDetailLevel = renderSource.getDataDetailLevel();
for (int relX = 0; relX < ColumnRenderSource.SECTION_SIZE; relX++)
{
for (int z = 0; z < ColumnRenderSource.SECTION_SIZE; z++)
for (int relZ = 0; relZ < ColumnRenderSource.SECTION_SIZE; relZ++)
{
// TODO make a config for this
// can be uncommented to limit the buffer building to a specific
// relative position in this section.
// useful for debugging a single column's rendering
// if (x != 0 || (z != 0 && z != 1))
// {
// continue;
// }
// stop the builder if requested
UncheckedInterruptedException.throwIfInterrupted();
ColumnArrayView columnRenderData = renderSource.getVerticalDataPointView(x, z);
// ignore empty/null columns
ColumnArrayView columnRenderData = renderSource.getVerticalDataPointView(relX, relZ);
if (columnRenderData.size() == 0
|| !RenderDataPointUtil.doesDataPointExist(columnRenderData.get(0))
|| RenderDataPointUtil.isVoid(columnRenderData.get(0)))
@@ -187,43 +188,66 @@ public class ColumnRenderBufferBuilder
continue;
}
ColumnRenderSource.DebugSourceFlag debugSourceFlag = renderSource.debugGetFlag(x, z);
ColumnArrayView[][] adjColumnViews = new ColumnArrayView[4][];
// We extract the adj data in the four cardinal direction
// we first reset the adjShadeDisabled. This is used to disable the shade on the
// border when we have transparent block like water or glass
// to avoid having a "darker border" underground
// Arrays.fill(adjShadeDisabled, false);
// We check every adj block in each direction
//=============//
// debug limit //
//=============//
// If the adj block is rendered in the same region and with same detail
// and is positioned in a place that is not going to be rendered by vanilla game
// then we can set this position as adj
// We avoid cases where the adjPosition is in player chunk while the position is
// not
// to always have a wall underwater
// can be used to limit the buffer building to a specific relative position.
// useful for debugging a single column
if (columnBuilderDebugEnabled)
{
int wantedX = Config.Client.Advanced.Debugging.columnBuilderDebugXRow.get();
if (wantedX >= 0 && relX != wantedX)
{
continue;
}
int wantedZ = Config.Client.Advanced.Debugging.columnBuilderDebugZRow.get();
if (wantedZ >= 0 && relZ != wantedZ)
{
continue;
}
}
//==================================//
// get adjacent render data columns //
//==================================//
ColumnArrayView[] adjColumnViews = new ColumnArrayView[EDhDirection.ADJ_DIRECTIONS.length];
for (EDhDirection lodDirection : EDhDirection.ADJ_DIRECTIONS)
{
try
{
int xAdj = x + lodDirection.getNormal().x;
int zAdj = z + lodDirection.getNormal().z;
boolean isCrossRegionBoundary =
int xAdj = relX + lodDirection.getNormal().x;
int zAdj = relZ + lodDirection.getNormal().z;
boolean isCrossRenderSourceBoundary =
(xAdj < 0 || xAdj >= ColumnRenderSource.SECTION_SIZE) ||
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
ColumnRenderSource adjRenderSource;
byte adjDetailLevel;
//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)
//=========================//
// get the adjacent render //
// source if present //
//=========================//
if (!isCrossRenderSourceBoundary)
{
//we compute at which detail that position should be rendered
// the adjacent position is inside this same render source
adjRenderSource = renderSource;
adjDetailLevel = thisDetailLevel;
}
else
{
// the adjacent position is outside this render source
// skip empty sections
adjRenderSource = adjRegions[lodDirection.ordinal() - 2];
if (adjRenderSource == null)
{
@@ -231,67 +255,70 @@ public class ColumnRenderBufferBuilder
}
adjDetailLevel = adjRenderSource.getDataDetailLevel();
if (adjDetailLevel != detailLevel)
{
//TODO: Implement this
}
else
if (adjDetailLevel == thisDetailLevel)
{
// if the adjacent position is outside this render source,
// wrap the position around so it's inside the adjacent source
if (xAdj < 0)
{
xAdj += ColumnRenderSource.SECTION_SIZE;
}
if (xAdj >= ColumnRenderSource.SECTION_SIZE)
{
xAdj -= ColumnRenderSource.SECTION_SIZE;
}
if (zAdj < 0)
{
zAdj += ColumnRenderSource.SECTION_SIZE;
if (xAdj >= ColumnRenderSource.SECTION_SIZE)
xAdj -= ColumnRenderSource.SECTION_SIZE;
}
if (zAdj >= ColumnRenderSource.SECTION_SIZE)
{
zAdj -= ColumnRenderSource.SECTION_SIZE;
}
}
}
else
{
adjRenderSource = renderSource;
adjDetailLevel = detailLevel;
}
if (adjDetailLevel < detailLevel - 1 || adjDetailLevel > detailLevel + 1)
{
continue;
}
if (adjDetailLevel == detailLevel || adjDetailLevel > detailLevel)
{
adjColumnViews[lodDirection.ordinal() - 2] = new ColumnArrayView[1];
adjColumnViews[lodDirection.ordinal() - 2][0] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
}
else
{
adjColumnViews[lodDirection.ordinal() - 2] = new ColumnArrayView[2];
adjColumnViews[lodDirection.ordinal() - 2][0] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
adjColumnViews[lodDirection.ordinal() - 2][1] = adjRenderSource.getVerticalDataPointView(
xAdj + (lodDirection.getAxis() == EDhDirection.Axis.X ? 0 : 1),
zAdj + (lodDirection.getAxis() == EDhDirection.Axis.Z ? 0 : 1));
}
//========================//
// get the adjacent views //
//========================//
// the old logic handled additional cases, but they never appeared to fire,
// so just these two cases should be fine
LodUtil.assertTrue(adjDetailLevel == thisDetailLevel || adjDetailLevel > thisDetailLevel);
adjColumnViews[lodDirection.ordinal() - 2] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
}
catch (RuntimeException e)
{
EVENT_LOGGER.warn("Failed to get adj data for [" + detailLevel + ":" + x + "," + z + "] at [" + lodDirection + "], Error: "+e.getMessage(), e);
EVENT_LOGGER.warn("Failed to get adj data for relative pos: [" + thisDetailLevel + ":" + relX + "," + relZ + "] at [" + lodDirection + "], Error: "+e.getMessage(), e);
}
} // for adjacent directions
//==========================//
// build this render column //
//==========================//
ColumnRenderSource.DebugSourceFlag debugSourceFlag = renderSource.debugGetFlag(relX, relZ);
// We render every vertical lod present in this position
// We only stop when we find a block that is void or non-existing block
for (int i = 0; i < columnRenderData.size(); i++)
{
// TODO make a config for this
// can be uncommented to limit which vertical LOD is generated
// if (i != 0)
// {
// continue;
// }
if (Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get())
{
int wantedColumnIndex = Config.Client.Advanced.Debugging.columnBuilderDebugColumnIndex.get();
if (wantedColumnIndex >= 0 && i != wantedColumnIndex)
{
continue;
}
}
long data = columnRenderData.get(i);
// If the data is not render-able (Void or non-existing) we stop since there is
@@ -304,8 +331,11 @@ public class ColumnRenderBufferBuilder
long topDataPoint = (i - 1) >= 0 ? columnRenderData.get(i - 1) : RenderDataPointUtil.EMPTY_DATA;
long bottomDataPoint = (i + 1) < columnRenderData.size() ? columnRenderData.get(i + 1) : RenderDataPointUtil.EMPTY_DATA;
CubicLodTemplate.addLodToBuffer(data, topDataPoint, bottomDataPoint, adjColumnViews, detailLevel,
x, z, quadBuilder, debugMode, debugSourceFlag);
addLodToBuffer(
data, topDataPoint, bottomDataPoint,
adjColumnViews,
thisDetailLevel, relX, relZ,
quadBuilder, debugSourceFlag);
}
}// for z
@@ -313,33 +343,146 @@ public class ColumnRenderBufferBuilder
quadBuilder.finalizeData();
}
//=================//
// vbo interaction //
//=================//
public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize)
private static void addLodToBuffer(
long data, long topData, long bottomData,
ColumnArrayView[] adjColumnViews,
byte detailLevel, int renderSourceOffsetPosX, int renderSourceOffsetPosZ,
LodQuadBuilder quadBuilder, ColumnRenderSource.DebugSourceFlag debugSource)
{
if (vbos.length == newSize)
long sectionPos = DhSectionPos.encode(detailLevel, renderSourceOffsetPosX, renderSourceOffsetPosZ);
short width = (short) BitShiftUtil.powerOfTwo(detailLevel);
short x = (short) DhSectionPos.getMinCornerBlockX(sectionPos);
short yMin = RenderDataPointUtil.getYMin(data);
short z = (short) DhSectionPos.getMinCornerBlockZ(sectionPos);
short ySize = (short) (RenderDataPointUtil.getYMax(data) - yMin);
if (ySize == 0)
{
return vbos;
return;
}
else if (ySize < 0)
{
throw new IllegalArgumentException("Negative y size for the data! Data: [" + RenderDataPointUtil.toString(data) + "].");
}
GLVertexBuffer[] newVbos = new GLVertexBuffer[newSize];
System.arraycopy(vbos, 0, newVbos, 0, Math.min(vbos.length, newSize));
if (newSize < vbos.length)
byte blockMaterialId = RenderDataPointUtil.getBlockMaterialId(data);
int color;
boolean fullBright = false;
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRendering.get();
switch (debugging)
{
for (int i = newSize; i < vbos.length; i++)
case OFF:
{
if (vbos[i] != null)
float saturationMultiplier = Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier.get().floatValue();
float brightnessMultiplier = Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier.get().floatValue();
if (saturationMultiplier == 1.0 && brightnessMultiplier == 1.0)
{
vbos[i].close();
color = RenderDataPointUtil.getColor(data);
}
else
{
float[] ahsv = ColorUtil.argbToAhsv(RenderDataPointUtil.getColor(data));
color = ColorUtil.ahsvToArgb(ahsv[0], ahsv[1], ahsv[2] * saturationMultiplier, ahsv[3] * brightnessMultiplier);
}
break;
}
case SHOW_DETAIL:
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel];
fullBright = true;
break;
}
case SHOW_BLOCK_MATERIAL:
{
switch (EDhApiBlockMaterial.getFromIndex(blockMaterialId))
{
case UNKNOWN:
case AIR: // shouldn't normally be rendered, but just in case
color = ColorUtil.HOT_PINK;
break;
case LEAVES:
color = ColorUtil.DARK_GREEN;
break;
case STONE:
color = ColorUtil.GRAY;
break;
case WOOD:
color = ColorUtil.BROWN;
break;
case METAL:
color = ColorUtil.DARK_GRAY;
break;
case DIRT:
color = ColorUtil.LIGHT_BROWN;
break;
case LAVA:
color = ColorUtil.ORANGE;
break;
case DEEPSLATE:
color = ColorUtil.BLACK;
break;
case SNOW:
color = ColorUtil.WHITE;
break;
case SAND:
color = ColorUtil.TAN;
break;
case TERRACOTTA:
color = ColorUtil.DARK_ORANGE;
break;
case NETHER_STONE:
color = ColorUtil.DARK_RED;
break;
case WATER:
color = ColorUtil.BLUE;
break;
case GRASS:
color = ColorUtil.GREEN;
break;
case ILLUMINATED:
color = ColorUtil.YELLOW;
break;
default:
// undefined color
color = ColorUtil.CYAN;
break;
}
fullBright = true;
break;
}
case SHOW_OVERLAPPING_QUADS:
{
color = ColorUtil.WHITE;
fullBright = true;
break;
}
case SHOW_RENDER_SOURCE_FLAG:
{
color = debugSource == null ? ColorUtil.RED : debugSource.color;
fullBright = true;
break;
}
default:
throw new IllegalArgumentException("Unknown debug mode: " + debugging);
}
return newVbos;
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
}
}
@@ -1,186 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
/**
* Builds LODs as rectangular prisms.
*
* @author James Seibel
* @version 2022-1-2
*/
public class CubicLodTemplate
{
public static void addLodToBuffer(
long data, long topData, long bottomData, ColumnArrayView[][] adjColumnViews,
byte detailLevel, int offsetPosX, int offsetOosZ, LodQuadBuilder quadBuilder,
EDhApiDebugRendering debugging, ColumnRenderSource.DebugSourceFlag debugSource)
{
DhLodPos blockOffsetPos = new DhLodPos(detailLevel, offsetPosX, offsetOosZ).convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL);
short width = (short) BitShiftUtil.powerOfTwo(detailLevel);
short x = (short) blockOffsetPos.x;
short yMin = RenderDataPointUtil.getYMin(data);
short z = (short) (short) blockOffsetPos.z;
short ySize = (short) (RenderDataPointUtil.getYMax(data) - yMin);
if (ySize == 0)
{
return;
}
else if (ySize < 0)
{
throw new IllegalArgumentException("Negative y size for the data! Data: " + RenderDataPointUtil.toString(data));
}
byte blockMaterialId = RenderDataPointUtil.getBlockMaterialId(data);
int color;
boolean fullBright = false;
switch (debugging)
{
case OFF:
{
float saturationMultiplier = Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier.get().floatValue();
float brightnessMultiplier = Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier.get().floatValue();
if (saturationMultiplier == 1.0 && brightnessMultiplier == 1.0)
{
color = RenderDataPointUtil.getColor(data);
}
else
{
float[] ahsv = ColorUtil.argbToAhsv(RenderDataPointUtil.getColor(data));
color = ColorUtil.ahsvToArgb(ahsv[0], ahsv[1], ahsv[2] * saturationMultiplier, ahsv[3] * brightnessMultiplier);
//ApiShared.LOGGER.info("Raw color:[{}], AHSV:{}, Out color:[{}]",
// ColorUtil.toString(DataPointUtil.getColor(data)),
// ahsv, ColorUtil.toString(color));
}
break;
}
case SHOW_DETAIL:
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel];
fullBright = true;
break;
}
case SHOW_BLOCK_MATERIAL:
{
switch (EDhApiBlockMaterial.getFromIndex(blockMaterialId))
{
case UNKNOWN:
case AIR: // shouldn't normally be rendered, but just in case
color = ColorUtil.HOT_PINK;
break;
case LEAVES:
color = ColorUtil.DARK_GREEN;
break;
case STONE:
color = ColorUtil.GRAY;
break;
case WOOD:
color = ColorUtil.BROWN;
break;
case METAL:
color = ColorUtil.DARK_GRAY;
break;
case DIRT:
color = ColorUtil.LIGHT_BROWN;
break;
case LAVA:
color = ColorUtil.ORANGE;
break;
case DEEPSLATE:
color = ColorUtil.BLACK;
break;
case SNOW:
color = ColorUtil.WHITE;
break;
case SAND:
color = ColorUtil.TAN;
break;
case TERRACOTTA:
color = ColorUtil.DARK_ORANGE;
break;
case NETHER_STONE:
color = ColorUtil.DARK_RED;
break;
case WATER:
color = ColorUtil.BLUE;
break;
case GRASS:
color = ColorUtil.GREEN;
break;
case ILLUMINATED:
color = ColorUtil.YELLOW;
break;
default:
// undefined color
color = ColorUtil.CYAN;
break;
}
fullBright = true;
break;
}
case SHOW_OVERLAPPING_QUADS:
{
color = ColorUtil.WHITE;
fullBright = true;
break;
}
case SHOW_RENDER_SOURCE_FLAG:
{
color = debugSource == null ? ColorUtil.RED : debugSource.color;
fullBright = true;
break;
}
default:
throw new IllegalArgumentException("Unknown debug mode: " + debugging);
}
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
}
}
@@ -39,8 +39,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.apache.logging.log4j.Logger;
//TODO: Recheck this class for refactoring
/**
* Used to create the quads before they are converted to render-able buffers. <br><br>
*
@@ -51,11 +49,6 @@ public class LodQuadBuilder
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
@Deprecated
public final boolean skipQuadsWithZeroSkylight;
@Deprecated
public final short skyLightCullingBelow;
@SuppressWarnings("unchecked")
private final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
@SuppressWarnings("unchecked")
@@ -134,8 +127,6 @@ public class LodQuadBuilder
this.transparentQuads[i] = new ArrayList<>();
}
this.skipQuadsWithZeroSkylight = false;
this.skyLightCullingBelow = 0;
this.clientLevelWrapper = clientLevelWrapper;
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get();
@@ -159,11 +150,6 @@ public class LodQuadBuilder
throw new IllegalArgumentException("addQuadAdj() is only for adj direction! Not UP or Down!");
}
if (this.skipQuadsWithZeroSkylight && skyLight == 0 && y + widthNorthSouthOrUpDown < this.skyLightCullingBelow)
{
return;
}
BufferQuad quad = new BufferQuad(x, y, z, widthEastWest, widthNorthSouthOrUpDown, color, irisBlockMaterialId, skyLight, blockLight, dir);
ArrayList<BufferQuad> quadList = (this.doTransparency && ColorUtil.getAlpha(color) < 255) ? this.transparentQuads[dir.ordinal()] : this.opaqueQuads[dir.ordinal()];
if (!quadList.isEmpty() &&
@@ -182,12 +168,6 @@ public class LodQuadBuilder
// XZ
public void addQuadUp(short x, short maxY, short z, short widthEastWest, short widthNorthSouthOrUpDown, int color, byte irisBlockMaterialId, byte skylight, byte blocklight) // TODO argument names are wrong
{
// cave culling
if (this.skipQuadsWithZeroSkylight && skylight == 0 && maxY < this.skyLightCullingBelow)
{
return;
}
BufferQuad quad = new BufferQuad(x, maxY, z, widthEastWest, widthNorthSouthOrUpDown, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.UP);
boolean isTransparent = (this.doTransparency && ColorUtil.getAlpha(color) < 255);
ArrayList<BufferQuad> quadList = isTransparent ? this.transparentQuads[EDhDirection.UP.ordinal()] : this.opaqueQuads[EDhDirection.UP.ordinal()];
@@ -209,15 +189,13 @@ public class LodQuadBuilder
public void addQuadDown(short x, short y, short z, short width, short wz, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
{
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
return;
BufferQuad quad = new BufferQuad(x, y, z, width, wz, color, irisBlockMaterialId, skylight, blocklight, EDhDirection.DOWN);
ArrayList<BufferQuad> qs = (doTransparency && ColorUtil.getAlpha(color) < 255)
? transparentQuads[EDhDirection.DOWN.ordinal()] : opaqueQuads[EDhDirection.DOWN.ordinal()];
if (!qs.isEmpty() &&
(qs.get(qs.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
if (!qs.isEmpty()
&& (qs.get(qs.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|| qs.get(qs.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
)
)
{
premergeCount++;
return;
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.dataObjects.render.columnViews;
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
@@ -28,41 +29,60 @@ import java.util.Arrays;
public final class ColumnArrayView implements IColumnDataView
{
public final LongArrayList data;
/**
* How many data points are currently being represented by this view. <br>
* Will be equal to or less than {@link ColumnArrayView#verticalSize}.
*/
public final int size;
public final int offset; // offset in longs
/** can be 0 if this column was created for an empty data source */
public final int vertSize; // vertical size in longs
/**
* Vertical size in data points. <Br>
* Can be 0 if this column was created for an empty data source.
*/
public final int verticalSize;
/**
* Where the relative starting index is in the {@link ColumnArrayView#data} array
* if this view is representing part of a {@link ColumnRenderSource}.
*/
public final int offset;
public ColumnArrayView(LongArrayList data, int size, int offset, int vertSize)
//=============//
// constructor //
//=============//
public ColumnArrayView(LongArrayList data, int size, int offset, int verticalSize)
{
this.data = data;
this.size = size;
this.offset = offset;
this.vertSize = vertSize;
this.verticalSize = verticalSize;
}
//=====================//
// getters and setters //
//=====================//
@Override
public long get(int index) { return data.getLong(index + offset); }
public void set(int index, long value) { data.set(index + offset, value); }
@Override
public int size() { return size; }
@Override
public int verticalSize() { return verticalSize; }
@Override
public int verticalSize() { return vertSize; }
@Override
public int dataCount() { return (this.vertSize != 0) ? (this.size / this.vertSize) : 0; }
public int dataCount() { return (this.verticalSize != 0) ? (this.size / this.verticalSize) : 0; } // TODO what does the divide by mean?
@Override
public ColumnArrayView subView(int dataIndexStart, int dataCount)
{
return new ColumnArrayView(data, dataCount * vertSize, offset + dataIndexStart * vertSize, vertSize);
return new ColumnArrayView(data, dataCount * verticalSize, offset + dataIndexStart * verticalSize, verticalSize);
}
public void fill(long value) { Arrays.fill(data.elements(), offset, offset + size, value); }
@@ -70,7 +90,7 @@ public final class ColumnArrayView implements IColumnDataView
public void copyFrom(IColumnDataView source) { copyFrom(source, 0); }
public void copyFrom(IColumnDataView source, int outputDataIndexOffset)
{
if (source.verticalSize() > vertSize)
if (source.verticalSize() > verticalSize)
{
throw new IllegalArgumentException("source verticalSize must be <= self's verticalSize to copy");
}
@@ -78,19 +98,19 @@ public final class ColumnArrayView implements IColumnDataView
{
throw new IllegalArgumentException("dataIndexStart + source.dataCount() must be <= self.dataCount() to copy");
}
else if (source.verticalSize() != vertSize)
else if (source.verticalSize() != verticalSize)
{
for (int i = 0; i < source.dataCount(); i++)
{
int outputOffset = offset + outputDataIndexOffset * vertSize + i * vertSize;
int outputOffset = offset + outputDataIndexOffset * verticalSize + i * verticalSize;
source.subView(i, 1).copyTo(data.elements(), outputOffset, source.verticalSize());
Arrays.fill(data.elements(), outputOffset + source.verticalSize(),
outputOffset + vertSize, 0);
outputOffset + verticalSize, 0);
}
}
else
{
source.copyTo(data.elements(), offset + outputDataIndexOffset * vertSize, source.size());
source.copyTo(data.elements(), offset + outputDataIndexOffset * verticalSize, source.size());
}
}
@@ -103,19 +123,19 @@ public final class ColumnArrayView implements IColumnDataView
{
throw new IllegalArgumentException("Cannot merge views of different sizes");
}
if (vertSize != source.vertSize)
if (verticalSize != source.verticalSize)
{
throw new IllegalArgumentException("Cannot merge views of different vertical sizes");
}
boolean anyChange = false;
for (int o = 0; o < (source.size() * vertSize); o += vertSize)
for (int o = 0; o < (source.size() * verticalSize); o += verticalSize)
{
if (override)
{
if (RenderDataPointUtil.compareDatapointPriority(source.get(o), get(o)) >= 0)
{
anyChange = true;
System.arraycopy(source.data, source.offset + o, data, offset + o, vertSize);
System.arraycopy(source.data, source.offset + o, data, offset + o, verticalSize);
}
}
else
@@ -123,7 +143,7 @@ public final class ColumnArrayView implements IColumnDataView
if (RenderDataPointUtil.compareDatapointPriority(source.get(o), get(o)) > 0)
{
anyChange = true;
System.arraycopy(source.data, source.offset + o, data, offset + o, vertSize);
System.arraycopy(source.data, source.offset + o, data, offset + o, verticalSize);
}
}
}
@@ -137,7 +157,7 @@ public final class ColumnArrayView implements IColumnDataView
throw new IllegalArgumentException("Cannot copy and resize to views with different dataCounts");
}
if (this.vertSize >= source.verticalSize())
if (this.verticalSize >= source.verticalSize())
{
this.copyFrom(source);
}
@@ -160,12 +180,18 @@ public final class ColumnArrayView implements IColumnDataView
RenderDataPointUtil.mergeMultiData(source, this);
}
//================//
// base overrides //
//================//
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("S:").append(size);
sb.append(" V:").append(vertSize);
sb.append(" V:").append(verticalSize);
sb.append(" O:").append(offset);
sb.append(" [");
@@ -182,6 +208,7 @@ public final class ColumnArrayView implements IColumnDataView
return sb.toString();
}
public int getDataHash()
{
return arrayHash(data, offset, size);
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.core.dataObjects.transformers;
import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
@@ -122,7 +121,7 @@ public class FullDataToRenderDataTransformer
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
LongArrayList dataColumn = fullDataSource.get(x, z);
updateRenderDataViewWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn);
updateOrReplaceRenderDataViewColumnWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn);
}
}
@@ -132,34 +131,41 @@ public class FullDataToRenderDataTransformer
}
/** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */
public static void updateRenderDataViewWithFullDataColumn(
public static void updateOrReplaceRenderDataViewColumnWithFullDataColumn(
IDhClientLevel level,
FullDataPointIdMap fullDataMapping, 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)
{
return;
}
int dataTotalLength = fullDataColumn.size();
if (dataTotalLength > columnArrayView.verticalSize())
int fullDataLength = fullDataColumn.size();
if (fullDataLength <= columnArrayView.verticalSize())
{
ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength);
iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn);
columnArrayView.changeVerticalSizeFrom(totalColumnData);
// Directly use the arrayView since it fits.
setRenderColumnView(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn);
}
else
{
iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits.
// expand the ColumnArrayView to fit the new larger max vertical size
ColumnArrayView newColumnArrayView = new ColumnArrayView(new LongArrayList(new long[fullDataLength]), fullDataLength, 0, fullDataLength);
setRenderColumnView(level, fullDataMapping, blockX, blockZ, newColumnArrayView, fullDataColumn);
columnArrayView.changeVerticalSizeFrom(newColumnArrayView);
}
}
private static void iterateAndConvert(
private static void setRenderColumnView(
IDhClientLevel level, FullDataPointIdMap fullDataMapping,
int blockX, int blockZ,
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
{
//===============//
// config values //
//===============//
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
@@ -176,30 +182,35 @@ public class FullDataToRenderDataTransformer
&& !level.getLevelWrapper().getDimensionType().isTheEnd()
);
boolean isVoid = true;
boolean isColumnVoid = true;
int colorToApplyToNextBlock = -1;
int lastColor = 0;
int lastBottom = -10000;
int lastBottom = -10_000;
int skylightToApplyToNextBlock = -1;
int blocklightToApplyToNextBlock = -1;
int columnOffset = 0;
IBiomeWrapper biome = null;
IBlockStateWrapper block = null;
//==================================//
// convert full data to render data //
//==================================//
// goes from the top down
for (int i = 0; i < fullColumnData.size(); i++)
{
long fullData = fullColumnData.getLong(i);
int bottomY = FullDataPointUtil.getBottomY(fullData);
int blockHeight = FullDataPointUtil.getHeight(fullData);
int id = FullDataPointUtil.getId(fullData);
int blockLight = FullDataPointUtil.getBlockLight(fullData);
int skyLight = FullDataPointUtil.getSkyLight(fullData);
IBiomeWrapper biome;
IBlockStateWrapper block;
try
{
biome = fullDataMapping.getBiomeWrapper(id);
@@ -207,7 +218,6 @@ public class FullDataToRenderDataTransformer
}
catch (IndexOutOfBoundsException e)
{
// FIXME sometimes the data map has a length of 0
if (!brokenPos.contains(fullDataMapping.getPos()))
{
brokenPos.add(fullDataMapping.getPos());
@@ -219,11 +229,12 @@ public class FullDataToRenderDataTransformer
"Further errors for this position won't be logged.");
}
// skip rendering broken data
// don't render broken data
continue;
}
//====================//
// ignored block and //
// cave culling check //
@@ -272,27 +283,28 @@ public class FullDataToRenderDataTransformer
}
//===================//
// solid block check //
//===================//
if (ignoreNonCollidingBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
//=======================//
// non-solid block check //
//=======================//
if (ignoreNonCollidingBlocks
&& !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
{
if (colorBelowWithAvoidedBlocks)
{
int tempColor = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block);
// don't transfer the color when alpha is 0
// this prevents issues if grass is transparent
if (ColorUtil.getAlpha(tempColor) != 0)
{
// don't transfer alpha if for some reason grass is semi transparent
colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255);
skylightToApplyToNextBlock = skyLight;
blocklightToApplyToNextBlock = blockLight;
}
}
// don't add this block
// skip this non-colliding block
continue;
}
@@ -312,7 +324,13 @@ public class FullDataToRenderDataTransformer
blockLight = blocklightToApplyToNextBlock;
}
//check if they share a top-bottom face and if they have same color
//=============================//
// merge same-colored adjacent //
//=============================//
// check if they share a top-bottom face and if they have same color
if (color == lastColor && bottomY + blockHeight == lastBottom && columnOffset > 0)
{
//replace the previous block with new bottom
@@ -323,20 +341,19 @@ public class FullDataToRenderDataTransformer
else
{
// add the block
isVoid = false;
isColumnVoid = false;
long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getMaterialId());
renderColumnData.set(columnOffset, columnData);
columnOffset++;
}
lastBottom = bottomY;
lastColor = color;
}
if (isVoid)
if (isColumnVoid)
{
renderColumnData.set(0, RenderDataPointUtil.createVoidDataPoint());
renderColumnData.set(0, RenderDataPointUtil.EMPTY_DATA);
}
}
@@ -152,8 +152,8 @@ public class LodDataBuilder
else
{
//we are at the height limit. There are no torches here, and sky is not obscured.
blockLight = 0;
skyLight = 15;
blockLight = LodUtil.MIN_MC_LIGHT;
skyLight = LodUtil.MAX_MC_LIGHT;
}
@@ -258,6 +258,8 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
public void clearRenderCache()
{
this.clientLevel.getClientLevelWrapper().clearBlockColorCache();
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState != null && ClientRenderState.quadtree != null)
{
@@ -237,7 +237,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
//================//
@Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return this.levelWrapper.computeBaseColor(pos, biome, block); }
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return this.levelWrapper.getBlockColor(pos, biome, block); }
@Override
public IClientLevelWrapper getClientLevelWrapper() { return this.levelWrapper; }
@@ -146,7 +146,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
}
else
{
return clientLevel.computeBaseColor(pos, biome, block);
return clientLevel.getBlockColor(pos, biome, block);
}
}
@@ -154,8 +154,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
public IClientLevelWrapper getClientLevelWrapper() { return MC_CLIENT.getWrappedClientLevel(); }
@Override
public void clearRenderCache()
{
public void clearRenderCache() {
this.clientside.clearRenderCache();
}
@@ -74,7 +74,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
*/
private final ConcurrentLinkedQueue<Long> sectionsToReload = new ConcurrentLinkedQueue<>();
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
private final ConfigChangeListener<EDhApiHorizontalQuality> horizontalScaleChangeListener;
private final ReentrantLock treeReadWriteLock = new ReentrantLock();
private final AtomicBoolean fullDataRetrievalQueueRunning = new AtomicBoolean(false);
@@ -110,8 +109,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
this.level = level;
this.fullDataSourceProvider = fullDataSourceProvider;
this.blockRenderDistanceDiameter = viewDiameterInBlocks;
this.horizontalScaleChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality, (newHorizontalScale) -> this.onHorizontalQualityChange());
}
@@ -151,7 +148,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
catch (Exception e)
{
LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getClientLevelWrapper().getDimensionName() + ", exception: " + e.getMessage(), e);
LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getLevelWrapper().getDimensionName() + ", exception: " + e.getMessage(), e);
}
finally
{
@@ -293,7 +290,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
//byte expectedDetailLevel = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL + 3; // can be used instead of the following logic for testing
byte expectedDetailLevel = this.calculateExpectedDetailLevel(playerPos, sectionPos);
expectedDetailLevel = (byte) Math.min(expectedDetailLevel, this.minRenderDetailLevel);
expectedDetailLevel += DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL;
expectedDetailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
if (DhSectionPos.getDetailLevel(sectionPos) > expectedDetailLevel)
@@ -615,7 +612,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// config listeners //
//==================//
private void onHorizontalQualityChange() { this.clearRenderDataCache(); }
private void onHorizontalQualityChange() { /*this.clearRenderDataCache();*/ }
//===========//
@@ -678,8 +675,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
{
LOGGER.info("Shutting down " + LodQuadTree.class.getSimpleName() + "...");
this.horizontalScaleChangeListener.close();
DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
@@ -24,16 +24,19 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBufferBuilder;
import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
@@ -54,6 +57,7 @@ import java.util.concurrent.locks.ReentrantLock;
public class LodRenderSection implements IDebugRenderable, AutoCloseable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
@@ -188,11 +192,12 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
{
adjacentRenderSections[i] = adjLoadRefFutures[i].future.getNow(null);
}
ColumnRenderBufferBuilder.buildAndUploadBuffersAsync(this.level, renderSource, adjacentRenderSections).thenAccept((buffer) ->
ColumnRenderBufferBuilder.buildAndUploadBuffersAsync(this.level, renderSource, adjacentRenderSections)
.thenAccept((buffer) ->
{
// upload complete, clean up the old data if
this.renderBuffer = buffer;
this.canRender = true;
this.canRender = (buffer != null);
this.uploadRenderDataToGpuFuture = null;
@@ -240,10 +245,17 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
long adjPos = DhSectionPos.getAdjacentPos(this.pos, direction);
try
{
LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos);
if (adjRenderSection != null)
// 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))
{
futureArray[arrayIndex] = adjRenderSection.getRenderSourceAsync();
LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos);
if (adjRenderSection != null)
{
futureArray[arrayIndex] = adjRenderSection.getRenderSourceAsync();
}
}
}
catch (IndexOutOfBoundsException ignore) {}
@@ -306,6 +318,23 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
/**
* Note: can cause issues with neighboring LOD sections
* if only some (vs all) futures are canceled.
*/
public void cancelGpuUpload()
{
CompletableFuture<Void> future = this.uploadRenderDataToGpuFuture;
this.uploadRenderDataToGpuFuture = null;
if (future != null)
{
// interrupting the future speeds things up, but also causes
// some LODs to never load in properly
future.cancel(false);
}
}
//========================//
// getters and properties //
@@ -829,7 +829,7 @@ public class RenderDataPointReducingList
int size = view.size();
if (size <= 0)
{
return RenderDataPointUtil.createVoidDataPoint();
return RenderDataPointUtil.EMPTY_DATA;
}
long highestDataPoint;
@@ -849,7 +849,7 @@ public class RenderDataPointReducingList
}
}
//no visible segments, return void.
return RenderDataPointUtil.createVoidDataPoint();
return RenderDataPointUtil.EMPTY_DATA;
}
//second loop: merge the rest of the segments.
@@ -889,7 +889,7 @@ public class RenderDataPointReducingList
// so, if we didn't set any data points, add a void data point.
if (writeIndex == 0)
{
view.set(writeIndex++, RenderDataPointUtil.createVoidDataPoint());
view.set(writeIndex++, RenderDataPointUtil.EMPTY_DATA);
}
for (int size = view.size(); writeIndex < size; writeIndex++)
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.util;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.level.AbstractDhLevel;
import com.seibel.distanthorizons.core.logging.SpamReducedLogger;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
@@ -110,8 +111,6 @@ public class RenderDataPointUtil
// datapoint manipulation //
//========================//
public static long createVoidDataPoint() { return EMPTY_DATA; }
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int irisBlockMaterialId)
{
return createDataPoint(
@@ -249,7 +248,7 @@ public class RenderDataPointUtil
getBlue(dataPoint) +
" BL:" + getLightBlock(dataPoint) +
" SL:" + getLightSky(dataPoint) +
" BID:" + getBlockMaterialId(dataPoint);
" MAT:" + getBlockMaterialId(dataPoint) + "["+ EDhApiBlockMaterial.getFromIndex(getBlockMaterialId(dataPoint))+"]";
}
}
@@ -303,352 +302,6 @@ public class RenderDataPointUtil
list.reduce(output.verticalSize());
list.copyTo(output);
}
//old logic left here in case it's ever needed again.
/*
if (output.dataCount() != 1)
{
throw new IllegalArgumentException("output must be only reserved for one datapoint!");
}
int inputVerticalSize = sourceData.verticalSize();
int outputVerticalSize = output.verticalSize();
output.fill(0);
//dataCount indicate how many position we are merging in one position
int dataCount = sourceData.dataCount();
// We initialize the arrays that are going to be used
int heightAndDepthLength = (MAX_WORLD_Y_SIZE / 2 + 16) * 2;
short[] heightAndDepth = tLocalHeightAndDepth.get();
if (heightAndDepth == null || heightAndDepth.length != heightAndDepthLength)
{
heightAndDepth = new short[heightAndDepthLength];
tLocalHeightAndDepth.set(heightAndDepth);
}
byte genMode = getGenerationMode(sourceData.get(0));
if (genMode == 0)
{
genMode = 1; // FIXME: Hack to make the version 10 genMode never be 0.
}
boolean allEmpty = true;
boolean allVoid = true;
boolean limited = false;
boolean allDefault;
long singleData;
short yMin;
short yMax;
int count = 0;
int i;
int ii;
int[] indices = tLocalIndices.get();
if (indices == null || indices.length != dataCount)
{
indices = new int[dataCount];
tLocalIndices.set(indices);
}
Arrays.fill(indices, 0);
boolean[] increaseIndex = tLocalIncreaseIndex.get();
if (increaseIndex == null || increaseIndex.length != dataCount)
{
increaseIndex = new boolean[dataCount];
tLocalIncreaseIndex.set(increaseIndex);
}
boolean[] indexHandled = tLocalIndexHandled.get();
if (indexHandled == null || indexHandled.length != dataCount)
{
indexHandled = new boolean[dataCount];
tLocalIndexHandled.set(indexHandled);
}
long tempData;
for (int index = 0; index < dataCount; index++)
{
tempData = sourceData.get(index * inputVerticalSize);
allVoid = allVoid && RenderDataPointUtil.isVoid(tempData);
allEmpty = allEmpty && !RenderDataPointUtil.doesDataPointExist(tempData);
}
//We check if there is any data that's not empty or void
if (allEmpty)
{
return;
}
else if (allVoid)
{
output.set(0, createVoidDataPoint(genMode));
return;
}
//this check is used only to see if we have checked all the values in the array
boolean stillHasDataToCheck = true;
short prevDepth;
while (stillHasDataToCheck)
{
Arrays.fill(indexHandled, false);
boolean connected = true;
int newHeight = -10000;
int newDepth = -10000;
int tempYMax;
int tempYMin;
while (connected)
{
Arrays.fill(increaseIndex, false);
for (int index = 0; index < dataCount; index++)
{
if (indices[index] < inputVerticalSize)
{
tempData = sourceData.get(index * inputVerticalSize + indices[index]);
if (!RenderDataPointUtil.isVoid(tempData) && RenderDataPointUtil.doesDataPointExist(tempData))
{
tempYMax = RenderDataPointUtil.getYMax(tempData);
tempYMin = RenderDataPointUtil.getYMin(tempData);
if (tempYMin >= newHeight)
{
//First case
//the column we are checking is higher than the current column
newDepth = tempYMin;
newHeight = tempYMax;
Arrays.fill(increaseIndex, false);
Arrays.fill(indexHandled, false);
increaseIndex[index] = true;
indexHandled[index] = true;
}
else if ((tempYMin >= newDepth) && (tempYMax <= newHeight))
{
//the column we are checking is contained in the current column
//we simply increase this index
increaseIndex[index] = true;
indexHandled[index] = true;
}
else if (tempYMax > newHeight && tempYMin <= newDepth)
{
newDepth = tempYMin;
newHeight = tempYMax;
increaseIndex[index] = true;
indexHandled[index] = true;
}
else if (tempYMax > newDepth && tempYMax <= newHeight)
{
//the column we are checking touches the current column from the bottom
//for this reason we extend what's below
//We want to avoid to expend this column if it has already been expanded by
//this index
if (!indexHandled[index])
{
newDepth = tempYMin;
increaseIndex[index] = true;
indexHandled[index] = true;
}
}
else if (tempYMin < newHeight && tempYMin > newDepth)
{
//the column we are checking touches the current column from the top
//for this reason we extend the top
newHeight = tempYMax;
increaseIndex[index] = true;
}
}
else
{
indexHandled[index] = true;
}
}
}
//if we added any new data there is a chance that we could add more
//for this reason we would continue
//if no data is added than the column hasn't changed.
//for this reason we can start working on a new column
connected = false;
for (int index = 0; index < dataCount; index++)
{
if (increaseIndex[index])
{
connected = true;
indices[index]++;
}
}
}
//Now we add the height and depth data we extracted to the heightAndDepth array
if (newDepth != newHeight)
{
if (count != 0)
{
prevDepth = heightAndDepth[(count - 1) * 2 + 1];
if (newHeight > prevDepth)
{
newHeight = (short) Math.min(newHeight, prevDepth);
}
}
heightAndDepth[count * 2] = (short) newHeight;
heightAndDepth[count * 2 + 1] = (short) newDepth;
count++;
}
//Here we check the condition that makes the loop continue
//We stop the loop only if there is no more data to check
stillHasDataToCheck = false;
for (int index = 0; index < dataCount; index++)
{
if (indices[index] < inputVerticalSize)
{
tempData = sourceData.get(index * inputVerticalSize + indices[index]);
stillHasDataToCheck |= !RenderDataPointUtil.isVoid(tempData) && RenderDataPointUtil.doesDataPointExist(tempData);
}
}
}
//we limit the vertical portion to maxVerticalData
int j = 0;
while (count > outputVerticalSize)
{
limited = true;
ii = MAX_WORLD_Y_SIZE;
for (i = 0; i < count - 1; i++)
{
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
{
ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2];
j = i;
}
}
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
for (i = j + 1; i < count - 1; i++)
{
heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2];
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
}
//System.arraycopy(heightAndDepth, j + 1, heightAndDepth, j, count - j - 1);
count--;
}
//As standard the vertical lods are ordered from top to bottom
if (!limited && dataCount == 1) // This mean source vertSize < output vertSize AND both dataCount == 1
{
sourceData.copyTo(output.data, output.offset, output.vertSize);
}
else
{
//We want to efficiently memorize indexes
int[] dataIndexesCache = tDataIndexCache.get();
if (dataIndexesCache == null || dataIndexesCache.length != dataCount)
{
dataIndexesCache = new int[dataCount];
tDataIndexCache.set(dataIndexesCache);
}
Arrays.fill(dataIndexesCache, 0);
//For each lod height-depth value we have found we now want to generate the rest of the data
//by merging all lods at lower level that are contained inside the new ones
for (j = 0; j < count; j++)
{
//We firstly collect height and depth data
//this will be added to each realtive long DataPoint
yMax = heightAndDepth[j * 2];
yMin = heightAndDepth[j * 2 + 1];
//if both height and depth are at 0 then we finished
if ((yMin == 0 && yMax == 0) || j >= heightAndDepth.length / 2)
{
break;
}
//We initialize data useful for the merge
int numberOfChildren = 0;
allEmpty = true;
allVoid = true;
//We initialize all the new values that we are going to put in the dataPoint
int tempAlpha = 0;
int tempRed = 0;
int tempGreen = 0;
int tempBlue = 0;
int tempLightBlock = 0;
int tempLightSky = 0;
long data = 0;
//For each position that we want to merge
for (int index = 0; index < dataCount; index++)
{
//we scan the lods in the position from top to bottom
while (dataIndexesCache[index] < inputVerticalSize)
{
singleData = sourceData.get(index * inputVerticalSize + dataIndexesCache[index]);
if (doesDataPointExist(singleData) && !isVoid(singleData))
{
dataIndexesCache[index]++;
if ((yMin <= getYMin(singleData) && getYMin(singleData) < yMax)
|| (yMin < getYMax(singleData) && getYMax(singleData) <= yMax))
{
data = singleData;
break;
}
}
else
{
break;
}
}
if (!doesDataPointExist(data))
{
data = createVoidDataPoint(genMode);
}
if (doesDataPointExist(data))
{
allEmpty = false;
if (!isVoid(data))
{
numberOfChildren++;
allVoid = false;
tempAlpha = Math.max(getAlpha(data), tempAlpha);
tempRed += getRed(data) * getRed(data);
tempGreen += getGreen(data) * getGreen(data);
tempBlue += getBlue(data) * getBlue(data);
tempLightBlock += getLightBlock(data);
tempLightSky += getLightSky(data);
}
}
}
//we have at least 1 child
if (dataCount != 1)
{
tempRed = tempRed / numberOfChildren;
tempGreen = tempGreen / numberOfChildren;
tempBlue = tempBlue / numberOfChildren;
tempLightBlock = tempLightBlock / numberOfChildren;
tempLightSky = tempLightSky / numberOfChildren;
}
//data = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
//if (j > 0 && getColor(data) == getColor(dataPoint[j]))
//{
// add simplification at the end due to color
//}
output.set(j, createDataPoint(tempAlpha, (int) Math.sqrt(tempRed), (int) Math.sqrt(tempGreen), (int) Math.sqrt(tempBlue), yMax, yMin, tempLightSky, tempLightBlock, genMode));
}
}
*/
}
}
@@ -19,8 +19,6 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.world;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import org.jetbrains.annotations.Nullable;
@@ -34,10 +32,10 @@ public interface IClientLevelWrapper extends ILevelWrapper
@Nullable
IServerLevelWrapper tryGetServerSideWrapper();
int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState);
int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState);
/** @return -1 if there was a problem getting the color */
int getDirtBlockColor();
void clearBlockColorCache();
/** Will return null if there was an issue finding the biome. */
@Nullable
@@ -512,14 +512,20 @@
"distanthorizons.config.client.advanced.debugging.allowUnsafeValues.@tooltip":
"If enabled, very limited config input validation will be performed. \n\nWarning: enabling this can cause instability or crashing, use at your own risk. \nNote: this option isn't saved between sessions.",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugEnable":
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugEnable":
"Enable Column Builder Limiting",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugDetailLevel":
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugDetailLevel":
"Column Builder Limit - Detail Level",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugXPos":
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugXPos":
"Column Builder Limit - X Pos",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugZPos":
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugZPos":
"Column Builder Limit - Z Pos",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugXRow":
"Column Builder Limit - X Row",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugZRow":
"Column Builder Limit - Z Row",
"distanthorizons.config.client.advanced.debugging.columnBuilderDebugColumnIndex":
"Column Builder Limit - Col Index",
"distanthorizons.config.client.advanced.buffers":