This commit is contained in:
James Seibel
2025-11-08 08:14:03 -06:00
parent 7e04b12e37
commit c374bf7ca8
7 changed files with 360 additions and 323 deletions
@@ -23,17 +23,13 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pooling.AbstractPhantomArrayList;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnQuadView;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.concurrent.atomic.AtomicLong;
/**
* Stores the render data used to generate OpenGL buffers.
*
@@ -43,10 +39,8 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final boolean DO_SAFETY_CHECKS = ModInfo.IS_DEV_BUILD;
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
/** width of this data in columns */
public static final int SECTION_SIZE = BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET); // 64
/** measured in data columns */
public static final int WIDTH = 64;
public static final PhantomArrayListPool ARRAY_LIST_POOL = new PhantomArrayListPool("Render Source");
@@ -63,8 +57,6 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
private boolean isEmpty = true;
public AtomicLong localVersion = new AtomicLong(0); // used to track changes to the data source, so that buffers can be updated when necessary
//==============//
@@ -88,9 +80,9 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
this.verticalDataCount = maxVerticalSize;
this.renderDataContainer = this.pooledArraysCheckout.getLongArray(0, SECTION_SIZE * SECTION_SIZE * this.verticalDataCount);
this.renderDataContainer = this.pooledArraysCheckout.getLongArray(0, WIDTH * WIDTH * this.verticalDataCount);
this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
this.debugSourceFlags = new DebugSourceFlag[WIDTH * WIDTH];
}
@@ -99,19 +91,19 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
// datapoint manipulation //
//========================//
public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.renderDataContainer.getLong(posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex); }
public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.renderDataContainer.getLong(posX * WIDTH * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex); }
public ColumnArrayView getVerticalDataPointView(int posX, int posZ)
{
int offset = posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount;
int offset = posX * WIDTH * this.verticalDataCount + posZ * this.verticalDataCount;
// don't allow returning views that are outside this render source's bounds
if (offset >= this.renderDataContainer.size())
{
return null;
}
else if (posX < 0 || posX >= SECTION_SIZE
|| posZ < 0 || posZ >= SECTION_SIZE)
else if (posX < 0 || posX >= WIDTH
|| posZ < 0 || posZ >= WIDTH)
{
return null;
}
@@ -120,8 +112,8 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
offset, this.verticalDataCount);
}
public ColumnQuadView getFullQuadView() { return this.getQuadViewOverRange(0, 0, SECTION_SIZE, SECTION_SIZE); }
public ColumnQuadView getQuadViewOverRange(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(this.renderDataContainer, SECTION_SIZE, this.verticalDataCount, quadX, quadZ, quadXSize, quadZSize); }
public ColumnQuadView getFullQuadView() { return this.getQuadViewOverRange(0, 0, WIDTH, WIDTH); }
public ColumnQuadView getQuadViewOverRange(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(this.renderDataContainer, WIDTH, this.verticalDataCount, quadX, quadZ, quadXSize, quadZSize); }
@@ -131,9 +123,8 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
public Long getPos() { return this.pos; }
public Long getKey() { return this.pos; }
public String getKeyDisplayString() { return DhSectionPos.toString(this.pos); }
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - SECTION_SIZE_OFFSET); }
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); }
public boolean isEmpty() { return this.isEmpty; }
public void markNotEmpty() { this.isEmpty = false; }
@@ -147,15 +138,15 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
}
for (int x = 0; x < SECTION_SIZE; x++)
for (int x = 0; x < WIDTH; x++)
{
for (int z = 0; z < SECTION_SIZE; z++)
for (int z = 0; z < WIDTH; z++)
{
ColumnArrayView columnArrayView = this.getVerticalDataPointView(x,z);
for (int i = 0; i < columnArrayView.size; i++)
{
long dataPoint = columnArrayView.get(i);
if (!RenderDataPointUtil.isVoid(dataPoint))
if (!RenderDataPointUtil.hasZeroHeight(dataPoint))
{
return true;
}
@@ -179,12 +170,12 @@ public class ColumnRenderSource extends AbstractPhantomArrayList
{
for (int z = zStart; z < zStart + zWidth; z++)
{
this.debugSourceFlags[x * SECTION_SIZE + z] = flag;
this.debugSourceFlags[x * WIDTH + z] = flag;
}
}
}
public DebugSourceFlag debugGetFlag(int ox, int oz) { return this.debugSourceFlags[ox * SECTION_SIZE + oz]; }
public DebugSourceFlag debugGetFlag(int ox, int oz) { return this.debugSourceFlags[ox * WIDTH + oz]; }
@@ -23,38 +23,32 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
import com.seibel.distanthorizons.core.render.LodQuadTree;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.PerfRecorder;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
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.ArrayList;
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.
* that block position is covered/occluded by an adjacent block/column.
*/
private static final byte SKYLIGHT_COVERED = -2;
private static final byte SKYLIGHT_COVERED = -1;
public static final PhantomArrayListPool ARRAY_LIST_POOL = new PhantomArrayListPool("Column Box");
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;
});
@@ -64,7 +58,7 @@ public class ColumnBox
public static void addBoxQuadsToBuilder(
LodQuadBuilder builder, IDhClientLevel clientLevel,
short xSize, short ySize, short zSize,
short width, short yHeight,
short minX, short minY, short minZ,
int color, byte irisBlockMaterialId, byte skyLight, byte blockLight,
long topData, long bottomData, ColumnArrayView[] adjData, boolean[] isAdjDataSameDetailLevel)
@@ -73,9 +67,9 @@ public class ColumnBox
// variable setup //
//================//
short maxX = (short) (minX + xSize);
short maxY = (short) (minY + ySize);
short maxZ = (short) (minZ + zSize);
short maxX = (short) (minX + width);
short maxY = (short) (minY + yHeight);
short maxZ = (short) (minZ + width);
byte skyLightTop = skyLight;
byte skyLightBot = RenderDataPointUtil.doesDataPointExist(bottomData) ? RenderDataPointUtil.getLightSky(bottomData) : 0;
@@ -111,15 +105,15 @@ public class ColumnBox
if (!isTransparent && isTopTransparent && RenderDataPointUtil.doesDataPointExist(topData))
{
skyLightTop = (byte) MathUtil.clamp(0, 15 - (RenderDataPointUtil.getYMax(topData) - minY), 15);
ySize = (short) (RenderDataPointUtil.getYMax(topData) - minY - 1);
yHeight = (short) (RenderDataPointUtil.getYMax(topData) - minY - 1);
}
else if (isTransparent && !isBottomTransparent && RenderDataPointUtil.doesDataPointExist(bottomData))
{
minY = (short) (minY + ySize - 1);
ySize = 1;
minY = (short) (minY + yHeight - 1);
yHeight = 1;
}
maxY = (short) (minY + ySize);
maxY = (short) (minY + yHeight);
}
@@ -128,16 +122,20 @@ public class ColumnBox
// add top and bottom faces //
//==========================//
boolean skipTop = RenderDataPointUtil.doesDataPointExist(topData) && (RenderDataPointUtil.getYMin(topData) == maxY) && !isTopTransparent;
boolean skipTop = RenderDataPointUtil.doesDataPointExist(topData)
&& (RenderDataPointUtil.getYMin(topData) == maxY)
&& !isTopTransparent;
if (!skipTop)
{
builder.addQuadUp(minX, maxY, minZ, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
builder.addQuadUp(minX, maxY, minZ, width, width, ColorUtil.applyShade(color, MC.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
}
boolean skipBottom = RenderDataPointUtil.doesDataPointExist(bottomData) && (RenderDataPointUtil.getYMax(bottomData) == minY) && !isBottomTransparent;
boolean skipBottom = RenderDataPointUtil.doesDataPointExist(bottomData)
&& (RenderDataPointUtil.getYMax(bottomData) == minY)
&& !isBottomTransparent;
if (!skipBottom)
{
builder.addQuadDown(minX, minY, minZ, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
builder.addQuadDown(minX, minY, minZ, width, width, ColorUtil.applyShade(color, MC.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
}
@@ -156,12 +154,12 @@ public class ColumnBox
// Add an adjacent face if this is opaque face or transparent over the void.
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.NORTH, minX, minY, minZ, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
builder.addQuadAdj(EDhDirection.NORTH, minX, minY, minZ, width, yHeight, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
}
else
{
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH, minX, minY, minZ, xSize, ySize,
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH, minX, minY, minZ, width, yHeight,
color, irisBlockMaterialId, blockLight);
}
}
@@ -174,12 +172,12 @@ public class ColumnBox
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.SOUTH, minX, minY, maxZ, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
builder.addQuadAdj(EDhDirection.SOUTH, minX, minY, maxZ, width, yHeight, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
}
else
{
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH, minX, minY, maxZ, xSize, ySize,
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH, minX, minY, maxZ, width, yHeight,
color, irisBlockMaterialId, blockLight);
}
}
@@ -192,12 +190,12 @@ public class ColumnBox
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.WEST, minX, minY, minZ, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
builder.addQuadAdj(EDhDirection.WEST, minX, minY, minZ, width, yHeight, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
}
else
{
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST, minX, minY, minZ, zSize, ySize,
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST, minX, minY, minZ, width, yHeight,
color, irisBlockMaterialId, blockLight);
}
}
@@ -210,19 +208,19 @@ public class ColumnBox
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.EAST, maxX, minY, minZ, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
builder.addQuadAdj(EDhDirection.EAST, maxX, minY, minZ, width, yHeight, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
}
else
{
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST, maxX, minY, minZ, zSize, ySize,
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST, maxX, minY, minZ, width, yHeight,
color, irisBlockMaterialId, blockLight);
}
}
}
private static void makeAdjVerticalQuad(
LodQuadBuilder builder, @NotNull ColumnArrayView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
LodQuadBuilder builder, @NotNull ColumnArrayView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
short x, short yMin, short z, short horizontalWidth, short ySize,
int color, byte irisBlockMaterialId, byte blockLight)
{
@@ -233,11 +231,9 @@ public class ColumnBox
color = ColorUtil.applyShade(color, MC.getShade(direction));
// 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)))
if (adjColumnView.size == 0
|| RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0)))
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
return;
}
@@ -245,165 +241,139 @@ public class ColumnBox
//===========================//
// Determine face visibility //
// based on it's neighbors //
// Build Y-range segments //
// with their sky light //
//===========================//
boolean transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && transparencyEnabled;
short yMax = (short) (yMin + ySize);
short yMax = (short) (yMin + ySize); // min is inclusive, max is exclusive
byte[] skyLightAtInputPos = THREAD_LOCAL_SKY_LIGHT_ARRAY.get();
// List to store segments: [startY, endY, skyLight]
ArrayList<YSegment> segments = new ArrayList<>();
try
int adjCount = adjColumnView.size();
// Start with the entire range at max light
segments.add(new YSegment(yMin, yMax, LodUtil.MAX_MC_LIGHT));
// Process each adjacent datapoint and split/update segments
for (int adjIndex = 0; adjIndex < adjCount; 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);
long adjPoint = adjColumnView.get(adjIndex);
short adjMinY = RenderDataPointUtil.getYMin(adjPoint);
short adjMaxY = RenderDataPointUtil.getYMax(adjPoint);
// iterate top down
int adjCount = adjColumnView.size();
for (int adjIndex = 0; adjIndex < adjCount; adjIndex++)
if (!RenderDataPointUtil.doesDataPointExist(adjPoint)
|| RenderDataPointUtil.hasZeroHeight(adjPoint)
|| yMax <= adjMinY)
{
long adjPoint = adjColumnView.get(adjIndex);
short adjMinY = RenderDataPointUtil.getYMin(adjPoint);
short adjMaxY = RenderDataPointUtil.getYMax(adjPoint);
// skip empty adjacent datapoints
if (!RenderDataPointUtil.doesDataPointExist(adjPoint)
|| RenderDataPointUtil.isVoid(adjPoint))
{
continue;
}
// skip this adjacent datapoint if it's above the input datapoint (since it can't affect the input data point)
if (yMax <= adjMinY)
{
continue;
}
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
&& transparencyEnabled;
//=================================//
// set sky light based on adjacent //
//=================================//
// set light based on overlapping adjacent
if (!adjTransparent)
{
// adj opaque
// mark positions adjacent is covering
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
for (int i = adjMinY; i < adjMaxY; i++)
{
byte skyLightAtPos = skyLightAtInputPos[i];
// if the adjacent is a different detail level, we want to render adjacent opaque
// faces to try and reduce the chance of holes on detail level borders
boolean adjacentCoversThis =
// if the adjacent is the same detail level, no special handling is necessary
!adjacentIsSameDetailLevel
// if the adjacent face is underground we probably don't need it
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
// check if this face is on a border
&&
(
(x == 0 && direction == EDhDirection.WEST)
|| (z == 0 && direction == EDhDirection.NORTH)
// TODO why does 256 represent a border? aren't LODs only 64 datapoints wide?
|| (x == 256 && direction == EDhDirection.EAST)
|| (z == 256 && direction == EDhDirection.SOUTH)
);
byte newSkyLightAtPos = adjacentCoversThis ? adjSkyLight : SKYLIGHT_COVERED;
skyLightAtInputPos[i] = (byte) Math.min(newSkyLightAtPos, skyLightAtPos);
}
}
else
{
// adjacent is transparent,
// use datapoint below adjacent for lighting
byte belowSkyLight = RenderDataPointUtil.getLightSky(adjBelowPoint);
for (int i = adjMinY; i < adjMaxY; i++)
{
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(belowSkyLight, skyLightAtPos);
}
}
// 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++)
{
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(adjSkyLight, skyLightAtPos);
}
continue;
}
long adjAbovePoint = (adjIndex != 0) ? adjColumnView.get(adjIndex - 1) : RenderDataPointUtil.EMPTY_DATA;
long adjBelowPoint = (adjIndex + 1 < adjCount) ? adjColumnView.get(adjIndex + 1) : RenderDataPointUtil.EMPTY_DATA;
boolean adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBelowPoint);
boolean adjTransparent = !adjOverVoid
&& RenderDataPointUtil.getAlpha(adjPoint) < 255
&& transparencyEnabled;
//=======================//
// create vertical faces //
//=======================//
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
byte lightToApply;
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && 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++)
if (!adjTransparent)
{
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
// Adjacent is opaque
boolean adjacentCoversThis =
!adjacentIsSameDetailLevel
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
&&
(
(x == 0 && direction == EDhDirection.WEST)
|| (z == 0 && direction == EDhDirection.NORTH)
|| (x == 256 && direction == EDhDirection.EAST)
|| (z == 256 && direction == EDhDirection.SOUTH)
);
lastSkyLight = skyLight;
quadBottomY = i;
}
quadTopY = (i + 1);
lightToApply = adjacentCoversThis ? adjSkyLight : SKYLIGHT_COVERED;
}
else
{
// Adjacent is transparent, use below light
lightToApply = RenderDataPointUtil.getLightSky(adjBelowPoint);
}
// add the in-progress face if present
if (quadTopY != -1)
// Apply light to the range [adjMinY, adjMaxY)
applyLightToRange(segments, adjMinY, adjMaxY, lightToApply);
// Fill overhang area [adjMaxY, adjAboveMinY) with adjSkyLight
int adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
if (adjMaxY < adjAboveMinY)
{
tryAddVerticalFaceWithSkyLightToBuilder(
builder, direction,
x, z, horizontalWidth,
color, irisBlockMaterialId, blockLight,
lastSkyLight, inputTransparent, quadTopY, quadBottomY
);
applyLightToRange(segments, adjMaxY, adjAboveMinY, adjSkyLight);
}
}
finally
//=======================//
// Create vertical faces //
// from segments //
//=======================//
for (YSegment seg : segments)
{
// 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);
tryAddVerticalFaceWithSkyLightToBuilder(
builder, direction,
x, z, horizontalWidth,
color, irisBlockMaterialId, blockLight,
seg.skyLight, inputTransparent, seg.endY, seg.startY
);
}
}
// Apply a light value to a Y range, splitting segments as needed
private static void applyLightToRange(ArrayList<YSegment> segments, int rangeStart, int rangeEnd, byte newLight)
{
ArrayList<YSegment> newSegments = new ArrayList<>();
for (YSegment seg : segments)
{
// No overlap
if (seg.endY <= rangeStart
|| seg.startY >= rangeEnd)
{
newSegments.add(seg);
continue;
}
// Partial or complete overlap - need to split
// Part before the range
if (seg.startY < rangeStart)
{
newSegments.add(new YSegment(seg.startY, rangeStart, seg.skyLight));
}
// Overlapping part - take minimum light
int overlapStart = Math.max(seg.startY, rangeStart);
int overlapEnd = Math.min(seg.endY, rangeEnd);
byte minLight = (byte) Math.min(newLight, seg.skyLight);
newSegments.add(new YSegment(overlapStart, overlapEnd, minLight));
// Part after the range
if (seg.endY > rangeEnd)
{
newSegments.add(new YSegment(rangeEnd, seg.endY, seg.skyLight));
}
}
segments.clear();
segments.addAll(newSegments);
}
private static void tryAddVerticalFaceWithSkyLightToBuilder(
LodQuadBuilder builder, EDhDirection direction,
short x, short z, short horizontalWidth,
@@ -412,24 +382,85 @@ public class ColumnBox
)
{
// invalid positions will have a negative skylight
if (lastSkyLight >= 0)
if (lastSkyLight < 0)
{
// 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);
}
}
return;
}
// 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))
{
return;
}
// don't add negative/empty height faces
short height = (short) (quadTopY - quadBottomY);
if (height <= 0)
{
return;
}
builder.addQuadAdj(
direction,
x, (short) quadBottomY, z,
horizontalWidth, height,
color, irisBlockMaterialId, lastSkyLight, blockLight);
}
private static class YSegment
{
int startY;
int endY;
byte skyLight;
YSegment(int startY, int endY, byte skyLight)
{
this.startY = startY;
this.endY = endY;
this.skyLight = skyLight;
}
}
/**
* @see com.seibel.distanthorizons.core.util.FullDataPointUtil
*/
private static class YSegmentUtil
{
private static final int HEIGHT_WIDTH = Short.SIZE;
private static final int SKY_LIGHT_WIDTH = Byte.SIZE;
private static final int START_Y_MASK = (int) Math.pow(2, HEIGHT_WIDTH) - 1;
private static final int END_Y_MASK = (int) Math.pow(2, HEIGHT_WIDTH) - 1;
private static final int SKY_LIGHT_MASK = (int) Math.pow(2, SKY_LIGHT_WIDTH) - 1;
private static final int START_Y_OFFSET = 0;
private static final int END_Y_OFFSET = START_Y_OFFSET + HEIGHT_WIDTH;
private static final int SKY_LIGHT_OFFSET = END_Y_OFFSET + HEIGHT_WIDTH;
public static long encode(short startY, short endY, byte skyLight)
{
long data = 0L;
data |= (long) (startY & START_Y_MASK) << START_Y_OFFSET;
data |= (long) (endY & END_Y_MASK) << END_Y_OFFSET;
data |= (long) (skyLight & SKY_LIGHT_MASK) << SKY_LIGHT_OFFSET;
return data;
}
public static short getStartY(long data) { return (short) ((data >> START_Y_OFFSET) & START_Y_MASK); }
public static short getEndY(long data) { return (short) ((data >> END_Y_OFFSET) & END_Y_MASK); }
public static short getSkyLight(long data) { return (short) ((data >> SKY_LIGHT_OFFSET) & SKY_LIGHT_MASK); }
}
}
@@ -33,7 +33,6 @@ import com.seibel.distanthorizons.core.render.glObject.GLProxy;
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.coreapi.util.BitShiftUtil;
@@ -106,18 +105,15 @@ public class ColumnRenderBufferBuilder
//===================//
byte thisDetailLevel = renderSource.getDataDetailLevel();
for (int relX = 0; relX < ColumnRenderSource.SECTION_SIZE; relX++)
for (int relX = 0; relX < ColumnRenderSource.WIDTH; relX++)
{
for (int relZ = 0; relZ < ColumnRenderSource.SECTION_SIZE; relZ++)
for (int relZ = 0; relZ < ColumnRenderSource.WIDTH; relZ++)
{
// stop the builder if requested
UncheckedInterruptedException.throwIfInterrupted();
// ignore empty/null columns
ColumnArrayView columnRenderData = renderSource.getVerticalDataPointView(relX, relZ);
if (columnRenderData.size() == 0
|| !RenderDataPointUtil.doesDataPointExist(columnRenderData.get(0))
|| RenderDataPointUtil.isVoid(columnRenderData.get(0)))
|| !RenderDataPointUtil.doesDataPointExist(columnRenderData.get(0))
|| RenderDataPointUtil.hasZeroHeight(columnRenderData.get(0)))
{
continue;
}
@@ -158,8 +154,8 @@ public class ColumnRenderBufferBuilder
int xAdj = relX + lodDirection.normal.x;
int zAdj = relZ + lodDirection.normal.z;
boolean isCrossRenderSourceBoundary =
(xAdj < 0 || xAdj >= ColumnRenderSource.SECTION_SIZE) ||
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
(xAdj < 0 || xAdj >= ColumnRenderSource.WIDTH) ||
(zAdj < 0 || zAdj >= ColumnRenderSource.WIDTH);
ColumnRenderSource adjRenderSource;
byte adjDetailLevel;
@@ -196,20 +192,20 @@ public class ColumnRenderBufferBuilder
if (xAdj < 0)
{
xAdj += ColumnRenderSource.SECTION_SIZE;
xAdj += ColumnRenderSource.WIDTH;
}
if (xAdj >= ColumnRenderSource.SECTION_SIZE)
if (xAdj >= ColumnRenderSource.WIDTH)
{
xAdj -= ColumnRenderSource.SECTION_SIZE;
xAdj -= ColumnRenderSource.WIDTH;
}
if (zAdj < 0)
{
zAdj += ColumnRenderSource.SECTION_SIZE;
zAdj += ColumnRenderSource.WIDTH;
}
if (zAdj >= ColumnRenderSource.SECTION_SIZE)
if (zAdj >= ColumnRenderSource.WIDTH)
{
zAdj -= ColumnRenderSource.SECTION_SIZE;
zAdj -= ColumnRenderSource.WIDTH;
}
}
}
@@ -244,15 +240,14 @@ public class ColumnRenderBufferBuilder
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++)
{
// can be uncommented to limit which vertical LOD is generated
if (Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugEnable.get())
{
int wantedColumnIndex = Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugColumnIndex.get();
if (wantedColumnIndex >= 0 && i != wantedColumnIndex)
if (wantedColumnIndex >= 0
&& i != wantedColumnIndex)
{
continue;
}
@@ -261,7 +256,8 @@ public class ColumnRenderBufferBuilder
long data = columnRenderData.get(i);
// If the data is not render-able (Void or non-existing) we stop since there is
// no data left in this position
if (RenderDataPointUtil.isVoid(data) || !RenderDataPointUtil.doesDataPointExist(data))
if (RenderDataPointUtil.hasZeroHeight(data)
|| !RenderDataPointUtil.doesDataPointExist(data))
{
break;
}
@@ -269,7 +265,7 @@ 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;
addLodToBuffer(
addRenderDataPointToBuilder(
clientLevel,
data, topDataPoint, bottomDataPoint,
adjColumnViews, isSameDetailLevel,
@@ -282,31 +278,31 @@ public class ColumnRenderBufferBuilder
quadBuilder.mergeQuads();
}
private static void addLodToBuffer(
private static void addRenderDataPointToBuilder(
IDhClientLevel clientLevel,
long data, long topData, long bottomData,
long renderData, long topRenderData, long bottomRenderData,
ColumnArrayView[] adjColumnViews, boolean[] isSameDetailLevel,
byte detailLevel, int renderSourceOffsetPosX, int renderSourceOffsetPosZ,
LodQuadBuilder quadBuilder, ColumnRenderSource.DebugSourceFlag debugSource)
{
long sectionPos = DhSectionPos.encode(detailLevel, renderSourceOffsetPosX, renderSourceOffsetPosZ);
short width = (short) BitShiftUtil.powerOfTwo(detailLevel);
short xMin = (short) DhSectionPos.getMinCornerBlockX(sectionPos);
short yMin = RenderDataPointUtil.getYMin(data);
short zMin = (short) DhSectionPos.getMinCornerBlockZ(sectionPos);
short ySize = (short) (RenderDataPointUtil.getYMax(data) - yMin);
short blockWidth = (short) DhSectionPos.getDetailLevelWidthInBlocks(detailLevel);
short blockMinX = (short) DhSectionPos.getMinCornerBlockX(sectionPos);
short blockMinY = RenderDataPointUtil.getYMin(renderData);
short blockMinZ = (short) DhSectionPos.getMinCornerBlockZ(sectionPos);
short blockMaxY = (short) (RenderDataPointUtil.getYMax(renderData) - blockMinY);
if (ySize == 0)
if (blockMaxY == 0)
{
return;
}
else if (ySize < 0)
else if (blockMaxY < 0)
{
throw new IllegalArgumentException("Negative y size for the data! Data: [" + RenderDataPointUtil.toString(data) + "].");
throw new IllegalArgumentException("Negative y size for the renderDataPoint! Data: [" + RenderDataPointUtil.toString(renderData) + "].");
}
byte blockMaterialId = RenderDataPointUtil.getBlockMaterialId(data);
byte blockMaterialId = RenderDataPointUtil.getBlockMaterialId(renderData);
@@ -321,11 +317,11 @@ public class ColumnRenderBufferBuilder
float brightnessMultiplier = Config.Client.Advanced.Graphics.Quality.brightnessMultiplier.get().floatValue();
if (saturationMultiplier == 1.0 && brightnessMultiplier == 1.0)
{
color = RenderDataPointUtil.getColor(data);
color = RenderDataPointUtil.getColor(renderData);
}
else
{
float[] ahsv = ColorUtil.argbToAhsv(RenderDataPointUtil.getColor(data));
float[] ahsv = ColorUtil.argbToAhsv(RenderDataPointUtil.getColor(renderData));
color = ColorUtil.ahsvToArgb(ahsv[0], ahsv[1], ahsv[2] * saturationMultiplier, ahsv[3] * brightnessMultiplier);
}
break;
@@ -416,13 +412,13 @@ public class ColumnRenderBufferBuilder
ColumnBox.addBoxQuadsToBuilder(
quadBuilder, clientLevel,
width, ySize, width,
xMin, yMin, zMin,
blockWidth, blockMaxY,
blockMinX, blockMinY, blockMinZ,
color,
blockMaterialId,
RenderDataPointUtil.getLightSky(data),
fullBright ? 15 : RenderDataPointUtil.getLightBlock(data),
topData, bottomData, adjColumnViews, isSameDetailLevel);
RenderDataPointUtil.getLightSky(renderData),
fullBright ? LodUtil.MAX_MC_LIGHT : RenderDataPointUtil.getLightBlock(renderData),
topRenderData, bottomRenderData, adjColumnViews, isSameDetailLevel);
}
}
@@ -138,7 +138,8 @@ public class LodQuadBuilder
//===========//
public void addQuadAdj(
EDhDirection dir, short x, short y, short z,
EDhDirection dir,
short x, short y, short z,
short widthEastWest, short widthNorthSouthOrUpDown,
int color, byte irisBlockMaterialId, byte skyLight, byte blockLight)
{
@@ -149,11 +150,11 @@ public class LodQuadBuilder
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() &&
(
quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|| quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
)
if (!quadList.isEmpty()
&& (
quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|| quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
)
{
this.premergeCount++;
return;
@@ -174,8 +175,8 @@ public class LodQuadBuilder
public void addQuadDown(short x, short y, short z, short width, short wz, int color, byte irisBlockMaterialId, byte skylight, byte blocklight)
{
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()];
ArrayList<BufferQuad> qs = (this.doTransparency && ColorUtil.getAlpha(color) < 255)
? this.transparentQuads[EDhDirection.DOWN.ordinal()] : this.opaqueQuads[EDhDirection.DOWN.ordinal()];
qs.add(quad);
}
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.pooling.PhantomArrayListCheckout;
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.render.LodQuadTree;
import com.seibel.distanthorizons.core.util.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
@@ -138,7 +139,7 @@ public class FullDataToRenderDataTransformer
}
}
columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL);
columnSource.fillDebugFlag(0, 0, ColumnRenderSource.WIDTH, ColumnRenderSource.WIDTH, ColumnRenderSource.DebugSourceFlag.FULL);
return columnSource;
}
@@ -173,7 +174,10 @@ public class FullDataToRenderDataTransformer
// expand the ColumnArrayView to fit the new larger max vertical size
ColumnArrayView newColumnArrayView = new ColumnArrayView(dataArrayList, fullDataLength, 0, fullDataLength);
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, newColumnArrayView, fullDataColumn);
PerfRecorder.Timer vertSize = LodQuadTree.TRANSFORM_PERF_RECORDER.start("vertSize");
columnArrayView.changeVerticalSizeFrom(newColumnArrayView);
vertSize.end();
}
finally
{
@@ -186,6 +190,8 @@ public class FullDataToRenderDataTransformer
int blockX, int blockZ,
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
{
PerfRecorder.Timer prep = LodQuadTree.TRANSFORM_PERF_RECORDER.start("prep");
//===============//
// config values //
//===============//
@@ -217,6 +223,8 @@ public class FullDataToRenderDataTransformer
int blocklightToApplyToNextBlock = -1;
int renderDataIndex = 0;
prep.end();
//==================================//
@@ -230,6 +238,8 @@ public class FullDataToRenderDataTransformer
// goes from the top down
for (int fullDataIndex = 0; fullDataIndex < fullColumnData.size(); fullDataIndex++)
{
PerfRecorder.Timer fullParse = LodQuadTree.TRANSFORM_PERF_RECORDER.start("fullParse");
long fullData = fullColumnData.getLong(fullDataIndex);
int bottomY = FullDataPointUtil.getBottomY(fullData);
@@ -265,6 +275,8 @@ public class FullDataToRenderDataTransformer
continue;
}
fullParse.end();
//====================//
@@ -272,48 +284,57 @@ public class FullDataToRenderDataTransformer
// cave culling check //
//====================//
boolean ignoreBlock = blockStatesToIgnore.contains(block);
boolean caveBlock = caveBlockStatesToIgnore.contains(block); // TODO caves should also ignore transparent/non-solid blocks (IE grass and plants) wthout each being defined
if (caveBlock)
PerfRecorder.Timer caveCull = LodQuadTree.TRANSFORM_PERF_RECORDER.start("caveCull");
try
{
if (caveCullingEnabled
// assume this data point is underground if it has no sky-light
&& skyLight == LodUtil.MIN_MC_LIGHT
// ignore caves above a certain height to prevent floating islands from having walls underneath them
&& topY < caveCullingMaxY
// cave culling shouldn't happen when at the top of the world
&& renderDataIndex != 0 && fullDataIndex != 0
// cave culling can't happen when at the bottom of the world
&& (fullDataIndex+1) < fullColumnData.size())
boolean ignoreBlock = blockStatesToIgnore.contains(block);
boolean caveBlock = caveBlockStatesToIgnore.contains(block); // TODO caves should also ignore transparent/non-solid blocks (IE grass and plants) wthout each being defined
if (caveBlock)
{
// we need to get the next sky/block lights because
// the air block here will always have a light of 0/0 due to only the top of the LOD's light being saved.
long nextFullData = fullColumnData.getLong(fullDataIndex+1);
int nextSkyLight = FullDataPointUtil.getSkyLight(nextFullData);
if (nextSkyLight == LodUtil.MIN_MC_LIGHT
&& ColorUtil.getAlpha(lastColor) == 255)
if (caveCullingEnabled
// assume this data point is underground if it has no sky-light
&& skyLight == LodUtil.MIN_MC_LIGHT
// ignore caves above a certain height to prevent floating islands from having walls underneath them
&& topY < caveCullingMaxY
// cave culling shouldn't happen when at the top of the world
&& renderDataIndex != 0 && fullDataIndex != 0
// cave culling can't happen when at the bottom of the world
&& (fullDataIndex + 1) < fullColumnData.size())
{
// replace the previous block with new bottom
long columnData = renderColumnData.get(renderDataIndex - 1);
columnData = RenderDataPointUtil.setYMin(columnData, bottomY);
renderColumnData.set(renderDataIndex - 1, columnData);
// we need to get the next sky/block lights because
// the air block here will always have a light of 0/0 due to only the top of the LOD's light being saved.
long nextFullData = fullColumnData.getLong(fullDataIndex + 1);
int nextSkyLight = FullDataPointUtil.getSkyLight(nextFullData);
if (nextSkyLight == LodUtil.MIN_MC_LIGHT
&& ColorUtil.getAlpha(lastColor) == 255)
{
// replace the previous block with new bottom
long columnData = renderColumnData.get(renderDataIndex - 1);
columnData = RenderDataPointUtil.setYMin(columnData, bottomY);
renderColumnData.set(renderDataIndex - 1, columnData);
}
continue;
}
continue;
if (ignoreBlock)
{
// this is a merged block and a cave block, so it should never be rendered
continue;
}
}
if (ignoreBlock)
else if (ignoreBlock)
{
// this is a merged block and a cave block, so it should never be rendered
// this is an ignored block, but shouldn't be merged like a cave block
continue;
}
}
else if (ignoreBlock)
finally
{
// this is an ignored block, but shouldn't be merged like a cave block
continue;
caveCull.end();
}
@@ -322,20 +343,22 @@ public class FullDataToRenderDataTransformer
// non-solid block check //
//=======================//
if (ignoreNonCollidingBlocks
&& !block.isSolid()
&& !block.isLiquid()
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
if (ignoreNonCollidingBlocks
&& !block.isSolid()
&& !block.isLiquid()
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
{
if (colorBelowWithAvoidedBlocks)
{
PerfRecorder.Timer nonSolid = LodQuadTree.TRANSFORM_PERF_RECORDER.start("color-NonSolid");
int tempColor = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
nonSolid.end();
// don't transfer the color when alpha is 0
// this prevents issues if grass is transparent
if (ColorUtil.getAlpha(tempColor) != 0)
{
colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255);
colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor, 255);
skylightToApplyToNextBlock = skyLight;
blocklightToApplyToNextBlock = blockLight;
}
@@ -349,8 +372,10 @@ public class FullDataToRenderDataTransformer
int color;
if (colorToApplyToNextBlock == -1)
{
PerfRecorder.Timer colorTimer = LodQuadTree.TRANSFORM_PERF_RECORDER.start("color");
// use this block's color
color = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
colorTimer.end();
}
else
{
@@ -366,6 +391,7 @@ public class FullDataToRenderDataTransformer
//=============================//
// merge same-colored adjacent //
//=============================//
PerfRecorder.Timer mergeSame = LodQuadTree.TRANSFORM_PERF_RECORDER.start("mergeSame");
// check if they share a top-bottom face and if they have same color
if (color == lastColor
@@ -387,6 +413,8 @@ public class FullDataToRenderDataTransformer
}
lastBottom = bottomY;
lastColor = color;
mergeSame.end();
}
@@ -398,21 +426,4 @@ public class FullDataToRenderDataTransformer
//================//
// helper methods //
//================//
/**
* Called in loops that may run for an extended period of time. <br>
* This is necessary to allow canceling these transformers since running
* them after the client has left a given world will throw exceptions.
*/
private static void throwIfThreadInterrupted() throws InterruptedException
{
if (Thread.interrupted())
{
throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted.");
}
}
}
@@ -59,8 +59,6 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRenderable, AutoCloseable
{
public static final byte TREE_LOWEST_DETAIL_LEVEL = ColumnRenderSource.SECTION_SIZE_OFFSET;
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** there should only ever be one {@link LodQuadTree} so having the thread static should be fine */
private static final ThreadPoolExecutor FULL_DATA_RETRIEVAL_QUEUE_THREAD = ThreadUtil.makeSingleThreadPool("QuadTree Full Data Retrieval Queue Populator");
@@ -99,11 +97,13 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// TODO should be removed once James is done testing
@Deprecated
public static final PerfRecorder FILE_PERF_RECORDER = new PerfRecorder("File");
@Deprecated
public static final PerfRecorder TRANSFORM_PERF_RECORDER = new PerfRecorder("Transform");
/** the smallest numerical detail level number that can be rendered */
private byte maxRenderDetailLevel;
private byte maxLeafRenderDetailLevel;
/** the largest numerical detail level number that can be rendered */
private byte minRenderDetailLevel;
private byte minRootRenderDetailLevel;
/** used to calculate when a detail drop will occur */
private double detailDropOffDistanceUnit;
@@ -121,7 +121,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
int initialPlayerBlockX, int initialPlayerBlockZ,
FullDataSourceProviderV2 fullDataSourceProvider)
{
super(viewDiameterInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), TREE_LOWEST_DETAIL_LEVEL);
super(viewDiameterInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
@@ -133,6 +133,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
FILE_PERF_RECORDER.clear();
TRANSFORM_PERF_RECORDER.clear();
COL_BOX_PERF_RECORDER.clear();
}
@@ -157,6 +159,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
}
FILE_PERF_RECORDER.tryLog();
TRANSFORM_PERF_RECORDER.tryLog();
COL_BOX_PERF_RECORDER.tryLog();
// this shouldn't be updated while the tree is being iterated through
this.updateDetailLevelVariables();
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.level.AbstractDhLevel;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.IColumnDataView;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -202,9 +203,9 @@ public class RenderDataPointUtil
return dataPoint & ~(HEIGHT_SHIFTED_MASK | DEPTH_SHIFTED_MASK) | height | depth;
}
/** AKA the ending/top/highest Y value above {@link AbstractDhLevel#getMinY()} */
/** AKA the ending/top/highest Y value above {@link ILevelWrapper#getMinHeight()} ()} */
public static short getYMax(long dataPoint) { return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK); }
/** AKA the starting/bottom/lowest Y value above {@link AbstractDhLevel#getMinY()} */
/** AKA the starting/bottom/lowest Y value above {@link ILevelWrapper#getMinHeight()} */
public static short getYMin(long dataPoint) { return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK); }
public static long setYMin(long dataPoint, int depth) { return (long) ((dataPoint & ~(DEPTH_MASK << DEPTH_SHIFT)) | (depth & DEPTH_MASK) << DEPTH_SHIFT); }
@@ -219,7 +220,7 @@ public class RenderDataPointUtil
public static byte getBlockMaterialId(long dataPoint) { return (byte) ((dataPoint >>> IRIS_BLOCK_MATERIAL_ID_SHIFT) & IRIS_BLOCK_MATERIAL_ID_MASK); }
public static boolean isVoid(long dataPoint) { return (((dataPoint >>> DEPTH_SHIFT) & HEIGHT_DEPTH_MASK) == 0); }
public static boolean hasZeroHeight(long dataPoint) { return (((dataPoint >>> DEPTH_SHIFT) & HEIGHT_DEPTH_MASK) == 0); }
public static boolean doesDataPointExist(long dataPoint) { return dataPoint != EMPTY_DATA; }
@@ -240,7 +241,7 @@ public class RenderDataPointUtil
{
return "null";
}
else if (isVoid(dataPoint))
else if (hasZeroHeight(dataPoint))
{
return "void";
}