Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside
This commit is contained in:
@@ -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>
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
+6
-1
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
+3
-5
@@ -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();
|
||||
|
||||
|
||||
+15
-2
@@ -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;
|
||||
|
||||
+56
-1
@@ -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;
|
||||
|
||||
|
||||
+3
-2
@@ -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()
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+209
-336
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+38
-9
@@ -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
|
||||
|
||||
+254
-111
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-186
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+3
-25
@@ -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;
|
||||
|
||||
+49
-22
@@ -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);
|
||||
|
||||
+45
-28
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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 //
|
||||
|
||||
+3
-3
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
+2
-4
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user