Fix transparent LODs over the void
This commit is contained in:
+50
-38
@@ -21,28 +21,33 @@ package com.seibel.lod.core.dataObjects.render.bufferBuilding;
|
||||
|
||||
import com.seibel.lod.core.enums.ELodDirection;
|
||||
|
||||
/**
|
||||
* Represents a render-able quad.
|
||||
*/
|
||||
/** Represents a render-able quad. */
|
||||
public final class BufferQuad
|
||||
{
|
||||
final short x;
|
||||
final short y;
|
||||
final short z;
|
||||
short widthEastWest;
|
||||
public final short x;
|
||||
public final short y;
|
||||
public final short z;
|
||||
|
||||
public short widthEastWest;
|
||||
/** This is both North/South and Up/Down since the merging logic is the same either way */
|
||||
short widthNorthSouthOrUpDown;
|
||||
final int color;
|
||||
final byte skyLight;
|
||||
final byte blockLight;
|
||||
final ELodDirection direction;
|
||||
|
||||
boolean hasError = false;
|
||||
public short widthNorthSouthOrUpDown;
|
||||
|
||||
/**
|
||||
* not final since it may need to be modified to be opaque
|
||||
* @see LodQuadBuilder#fixTransparencyOverVoid
|
||||
*/
|
||||
public int color;
|
||||
|
||||
public final byte skyLight;
|
||||
public final byte blockLight;
|
||||
public final ELodDirection direction;
|
||||
|
||||
public boolean hasError = false;
|
||||
|
||||
|
||||
|
||||
BufferQuad(short x, short y, short z, short widthEastWest, short widthNorthSouthOrUpDown,
|
||||
int color, byte skylight, byte blocklight,
|
||||
int color, byte skylight, byte blockLight,
|
||||
ELodDirection direction)
|
||||
{
|
||||
if (widthEastWest == 0 || widthNorthSouthOrUpDown == 0)
|
||||
@@ -57,7 +62,7 @@ public final class BufferQuad
|
||||
this.widthNorthSouthOrUpDown = widthNorthSouthOrUpDown;
|
||||
this.color = color;
|
||||
this.skyLight = skylight;
|
||||
this.blockLight = blocklight;
|
||||
this.blockLight = blockLight;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@@ -66,43 +71,43 @@ public final class BufferQuad
|
||||
/** a rough but fast calculation */
|
||||
double calculateDistance(double relativeX, double relativeY, double relativeZ)
|
||||
{
|
||||
return Math.pow(relativeX - x, 2) + Math.pow(relativeY - y, 2) + Math.pow(relativeZ - z, 2);
|
||||
return Math.pow(relativeX - this.x, 2) + Math.pow(relativeY - this.y, 2) + Math.pow(relativeZ - this.z, 2);
|
||||
}
|
||||
|
||||
/** compares this quad's position to the given quad */
|
||||
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
||||
{
|
||||
if (direction != quad.direction)
|
||||
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + direction);
|
||||
if (this.direction != quad.direction)
|
||||
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction);
|
||||
|
||||
if (compareDirection == BufferMergeDirectionEnum.EastWest)
|
||||
{
|
||||
switch (direction.getAxis())
|
||||
switch (this.direction.getAxis())
|
||||
{
|
||||
case X:
|
||||
return threeDimensionalCompare(x, y, z, quad.x, quad.y, quad.z);
|
||||
return threeDimensionalCompare(this.x, this.y, this.z, quad.x, quad.y, quad.z);
|
||||
case Y:
|
||||
return threeDimensionalCompare(y, z, x, quad.y, quad.z, quad.x);
|
||||
return threeDimensionalCompare(this.y, this.z, this.x, quad.y, quad.z, quad.x);
|
||||
case Z:
|
||||
return threeDimensionalCompare(z, y, x, quad.z, quad.y, quad.x);
|
||||
return threeDimensionalCompare(this.z, this.y, this.x, quad.z, quad.y, quad.x);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + direction.getAxis());
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + this.direction.getAxis());
|
||||
}
|
||||
}
|
||||
else // if ()
|
||||
else
|
||||
{
|
||||
switch (direction.getAxis())
|
||||
switch (this.direction.getAxis())
|
||||
{
|
||||
case X:
|
||||
return threeDimensionalCompare(x, z, y, quad.x, quad.z, quad.y);
|
||||
return threeDimensionalCompare(this.x, this.z, this.y, quad.x, quad.z, quad.y);
|
||||
case Y:
|
||||
return threeDimensionalCompare(y, x, z, quad.y, quad.x, quad.z);
|
||||
return threeDimensionalCompare(this.y, this.x, this.z, quad.y, quad.x, quad.z);
|
||||
case Z:
|
||||
return threeDimensionalCompare(z, x, y, quad.z, quad.x, quad.y);
|
||||
return threeDimensionalCompare(this.z, this.x, this.y, quad.z, quad.x, quad.y);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + direction.getAxis());
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + this.direction.getAxis());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +133,7 @@ public final class BufferQuad
|
||||
{
|
||||
if (quad.hasError || this.hasError) return false;
|
||||
// only merge quads that are in the same direction
|
||||
if (direction != quad.direction)
|
||||
if (this.direction != quad.direction)
|
||||
return false;
|
||||
|
||||
// make sure these quads share the same perpendicular axis
|
||||
@@ -228,9 +233,16 @@ public final class BufferQuad
|
||||
}
|
||||
|
||||
// FIXME: TEMP: Hard limit for width
|
||||
if (thisPerpendicularCompareWidth >= 16) return false;
|
||||
if (thisPerpendicularCompareWidth >= 16)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (Math.floorDiv(otherPerpendicularCompareStartPos, 16)
|
||||
!= Math.floorDiv(thisPerpendicularCompareStartPos, 16)) return false;
|
||||
!= Math.floorDiv(thisPerpendicularCompareStartPos, 16))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// check if these quads are adjacent
|
||||
if (thisPerpendicularCompareStartPos + thisPerpendicularCompareWidth < otherPerpendicularCompareStartPos ||
|
||||
@@ -257,9 +269,9 @@ public final class BufferQuad
|
||||
}
|
||||
|
||||
// do the quads' color, light, etc. match?
|
||||
if (color != quad.color ||
|
||||
skyLight != quad.skyLight ||
|
||||
blockLight != quad.blockLight)
|
||||
if (this.color != quad.color ||
|
||||
this.skyLight != quad.skyLight ||
|
||||
this.blockLight != quad.blockLight)
|
||||
{
|
||||
// we can only merge identically colored/lit quads
|
||||
return false;
|
||||
@@ -268,11 +280,11 @@ public final class BufferQuad
|
||||
// merge the two quads
|
||||
if (mergeDirection == BufferMergeDirectionEnum.NorthSouthOrUpDown)
|
||||
{
|
||||
widthNorthSouthOrUpDown += quad.widthNorthSouthOrUpDown;
|
||||
this.widthNorthSouthOrUpDown += quad.widthNorthSouthOrUpDown;
|
||||
}
|
||||
else // if (mergeDirection == MergeDirection.EastWest)
|
||||
{
|
||||
widthEastWest += quad.widthEastWest;
|
||||
this.widthEastWest += quad.widthEastWest;
|
||||
}
|
||||
|
||||
// merge successful
|
||||
|
||||
+279
-200
@@ -28,130 +28,172 @@ import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.coreapi.util.MathUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
|
||||
public class ColumnBox {
|
||||
public class ColumnBox
|
||||
{
|
||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
|
||||
public static void addBoxQuadsToBuilder(LodQuadBuilder builder, short xSize, short ySize, short zSize, short x,
|
||||
short y, short z, int color, byte skyLight, byte blockLight, long topData, long botData, ColumnArrayView[][] adjData) {
|
||||
|
||||
public static void addBoxQuadsToBuilder(
|
||||
LodQuadBuilder builder,
|
||||
short xSize, short ySize, short zSize,
|
||||
short x, short y, short z,
|
||||
int color, byte skyLight, byte blockLight,
|
||||
long topData, long bottomData, ColumnArrayView[][] adjData)
|
||||
{
|
||||
short maxX = (short) (x + xSize);
|
||||
short maxY = (short) (y + ySize);
|
||||
short maxZ = (short) (z + zSize);
|
||||
byte skyLightTop = skyLight;
|
||||
byte skyLightBot = RenderDataPointUtil.doesDataPointExist(botData) ? RenderDataPointUtil.getLightSky(botData) : 0;
|
||||
|
||||
byte skyLightBot = RenderDataPointUtil.doesDataPointExist(bottomData) ? RenderDataPointUtil.getLightSky(bottomData) : 0;
|
||||
|
||||
boolean isTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
|
||||
if (builder.skipQuadsWithZeroSkylight
|
||||
|
||||
if (builder.skipQuadsWithZeroSkylight
|
||||
&& 0 == skyLight
|
||||
&& builder.skyLightCullingBelow > maxY
|
||||
&& ((RenderDataPointUtil.getAlpha(topData) < 255 && RenderDataPointUtil.getHeight(topData) >= builder.skyLightCullingBelow)
|
||||
|| (RenderDataPointUtil.getDepth(topData) >= builder.skyLightCullingBelow)
|
||||
|| !RenderDataPointUtil.doesDataPointExist(topData))) {
|
||||
&&
|
||||
(
|
||||
(RenderDataPointUtil.getAlpha(topData) < 255 && RenderDataPointUtil.getHeight(topData) >= builder.skyLightCullingBelow)
|
||||
|| (RenderDataPointUtil.getDepth(topData) >= builder.skyLightCullingBelow)
|
||||
|| !RenderDataPointUtil.doesDataPointExist(topData)
|
||||
)
|
||||
)
|
||||
{
|
||||
maxY = builder.skyLightCullingBelow;
|
||||
}
|
||||
|
||||
boolean isTopTransparent = RenderDataPointUtil.getAlpha(topData) < 255 && LodRenderer.transparencyEnabled;
|
||||
boolean isBotTransparent = RenderDataPointUtil.getAlpha(botData) < 255 && LodRenderer.transparencyEnabled;
|
||||
|
||||
|
||||
// Up direction case
|
||||
//We skip if
|
||||
// current block is not transparent: we check if the adj block is attached and opaque
|
||||
|
||||
boolean skipTop = RenderDataPointUtil.doesDataPointExist(topData) && (RenderDataPointUtil.getDepth(topData) == maxY) && !isTopTransparent;
|
||||
boolean skipBot = RenderDataPointUtil.doesDataPointExist(botData) && (RenderDataPointUtil.getHeight(botData) == y) && !isBotTransparent;
|
||||
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor) {
|
||||
if (!isTransparent && isTopTransparent && RenderDataPointUtil.doesDataPointExist(topData)) {
|
||||
skyLightTop = (byte) MathUtil.clamp(0, 15 - (RenderDataPointUtil.getHeight(topData) - y), 15);
|
||||
ySize = (short) (RenderDataPointUtil.getHeight(topData) - y - 1);
|
||||
//y = (short) (DataPointUtil.getHeight(topData) - 2);
|
||||
//ySize = 1;
|
||||
} else if (isTransparent && !isBotTransparent && RenderDataPointUtil.doesDataPointExist(botData)) {
|
||||
y = (short) (y + ySize - 1);
|
||||
ySize = 1;
|
||||
}
|
||||
maxY = (short) (y + ySize);
|
||||
}
|
||||
|
||||
boolean isBottomTransparent = RenderDataPointUtil.getAlpha(bottomData) < 255 && LodRenderer.transparencyEnabled;
|
||||
|
||||
|
||||
|
||||
// fake ocean transparency
|
||||
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
|
||||
{
|
||||
if (!isTransparent && isTopTransparent && RenderDataPointUtil.doesDataPointExist(topData))
|
||||
{
|
||||
skyLightTop = (byte) MathUtil.clamp(0, 15 - (RenderDataPointUtil.getHeight(topData) - y), 15);
|
||||
ySize = (short) (RenderDataPointUtil.getHeight(topData) - y - 1);
|
||||
}
|
||||
else if (isTransparent && !isBottomTransparent && RenderDataPointUtil.doesDataPointExist(bottomData))
|
||||
{
|
||||
y = (short) (y + ySize - 1);
|
||||
ySize = 1;
|
||||
}
|
||||
|
||||
maxY = (short) (y + ySize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// add top and bottom faces if requested //
|
||||
|
||||
boolean skipTop = RenderDataPointUtil.doesDataPointExist(topData) && (RenderDataPointUtil.getDepth(topData) == maxY) && !isTopTransparent;
|
||||
if (!skipTop)
|
||||
builder.addQuadUp(x, maxY, z, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(ELodDirection.UP)), skyLightTop, blockLight);
|
||||
if (!skipBot)
|
||||
builder.addQuadDown(x, y, z, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(ELodDirection.DOWN)), skyLightBot, blockLight);
|
||||
|
||||
//if (isTransparent)
|
||||
// return;
|
||||
//If the adj pos is at the same level we cull the faces normally, otherwise we divide the face in two and cull the two part separately
|
||||
|
||||
{
|
||||
builder.addQuadUp(x, maxY, z, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(ELodDirection.UP)), skyLightTop, blockLight);
|
||||
}
|
||||
|
||||
boolean skipBottom = RenderDataPointUtil.doesDataPointExist(bottomData) && (RenderDataPointUtil.getHeight(bottomData) == y) && !isBottomTransparent;
|
||||
if (!skipBottom)
|
||||
{
|
||||
builder.addQuadDown(x, y, z, xSize, zSize, ColorUtil.applyShade(color, MC.getShade(ELodDirection.DOWN)), skyLightBot, blockLight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// add North, south, east, and west faces if requested //
|
||||
|
||||
//NORTH face vertex creation
|
||||
{
|
||||
ColumnArrayView[] adjDataNorth = adjData[ELodDirection.NORTH.ordinal() - 2];
|
||||
int adjOverlapNorth = ColorUtil.TRANSPARENT;
|
||||
if (adjDataNorth == null) {
|
||||
builder.addQuadAdj(ELodDirection.NORTH, x, y, z, xSize, ySize, color, (byte) 15, blockLight);
|
||||
} else if (adjDataNorth.length == 1) {
|
||||
makeAdjQuads(builder, adjDataNorth[0], ELodDirection.NORTH, x, y, z, xSize, ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight);
|
||||
} else {
|
||||
makeAdjQuads(builder, adjDataNorth[0], ELodDirection.NORTH, x, y, z, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight);
|
||||
makeAdjQuads(builder, adjDataNorth[1], ELodDirection.NORTH, (short) (x + xSize / 2), y, z, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight);
|
||||
}
|
||||
ColumnArrayView[] adjDataNorth = adjData[ELodDirection.NORTH.ordinal() - 2];
|
||||
int adjOverlapNorth = ColorUtil.INVISIBLE;
|
||||
if (adjDataNorth == null)
|
||||
{
|
||||
builder.addQuadAdj(ELodDirection.NORTH, x, y, z, xSize, ySize, color, (byte) 15, blockLight);
|
||||
}
|
||||
else if (adjDataNorth.length == 1)
|
||||
{
|
||||
makeAdjQuads(builder, adjDataNorth[0], ELodDirection.NORTH, x, y, z, xSize, ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
}
|
||||
else
|
||||
{
|
||||
makeAdjQuads(builder, adjDataNorth[0], ELodDirection.NORTH, x, y, z, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
makeAdjQuads(builder, adjDataNorth[1], ELodDirection.NORTH, (short) (x + xSize / 2), y, z, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapNorth, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
}
|
||||
}
|
||||
|
||||
//SOUTH face vertex creation
|
||||
{
|
||||
ColumnArrayView[] adjDataSouth = adjData[ELodDirection.SOUTH.ordinal() - 2];
|
||||
int adjOverlapSouth = ColorUtil.TRANSPARENT;
|
||||
int adjOverlapSouth = ColorUtil.INVISIBLE;
|
||||
if (adjDataSouth == null) {
|
||||
builder.addQuadAdj(ELodDirection.SOUTH, x, y, maxZ, xSize, ySize, color, (byte) 15, blockLight);
|
||||
} else if (adjDataSouth.length == 1) {
|
||||
makeAdjQuads(builder, adjDataSouth[0], ELodDirection.SOUTH, x, y, maxZ, xSize, ySize,
|
||||
color, adjOverlapSouth, skyLightTop, blockLight);
|
||||
color, adjOverlapSouth, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
} else {
|
||||
makeAdjQuads(builder, adjDataSouth[0], ELodDirection.SOUTH, x, y, maxZ, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapSouth, skyLightTop, blockLight);
|
||||
color, adjOverlapSouth, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
|
||||
makeAdjQuads(builder, adjDataSouth[1], ELodDirection.SOUTH, (short) (x + xSize / 2), y, maxZ, (short) (xSize / 2), ySize,
|
||||
color, adjOverlapSouth, skyLightTop, blockLight);
|
||||
color, adjOverlapSouth, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
}
|
||||
}
|
||||
|
||||
//WEST face vertex creation
|
||||
{
|
||||
ColumnArrayView[] adjDataWest = adjData[ELodDirection.WEST.ordinal() - 2];
|
||||
int adjOverlapWest = ColorUtil.TRANSPARENT;
|
||||
int adjOverlapWest = ColorUtil.INVISIBLE;
|
||||
if (adjDataWest == null) {
|
||||
builder.addQuadAdj(ELodDirection.WEST, x, y, z, zSize, ySize, color, (byte) 15, blockLight);
|
||||
} else if (adjDataWest.length == 1) {
|
||||
makeAdjQuads(builder, adjDataWest[0], ELodDirection.WEST, x, y, z, zSize, ySize,
|
||||
color, adjOverlapWest, skyLightTop, blockLight);
|
||||
color, adjOverlapWest, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
} else {
|
||||
makeAdjQuads(builder, adjDataWest[0], ELodDirection.WEST, x, y, z, (short) (zSize / 2), ySize,
|
||||
color, adjOverlapWest, skyLightTop, blockLight);
|
||||
color, adjOverlapWest, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
makeAdjQuads(builder, adjDataWest[1], ELodDirection.WEST, x, y, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
|
||||
color, adjOverlapWest, skyLightTop, blockLight);
|
||||
color, adjOverlapWest, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
}
|
||||
}
|
||||
|
||||
//EAST face vertex creation
|
||||
{
|
||||
ColumnArrayView[] adjDataEast = adjData[ELodDirection.EAST.ordinal() - 2];
|
||||
int adjOverlapEast = ColorUtil.TRANSPARENT;
|
||||
int adjOverlapEast = ColorUtil.INVISIBLE;
|
||||
if (adjData[ELodDirection.EAST.ordinal() - 2] == null) {
|
||||
builder.addQuadAdj(ELodDirection.EAST, maxX, y, z, zSize, ySize, color, (byte) 15, blockLight);
|
||||
} else if (adjDataEast.length == 1) {
|
||||
makeAdjQuads(builder, adjDataEast[0], ELodDirection.EAST, maxX, y, z, zSize, ySize,
|
||||
color, adjOverlapEast, skyLightTop, blockLight);
|
||||
color, adjOverlapEast, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
} else {
|
||||
makeAdjQuads(builder, adjDataEast[0], ELodDirection.EAST, maxX, y, z, (short) (zSize / 2), ySize,
|
||||
color, adjOverlapEast, skyLightTop, blockLight);
|
||||
color, adjOverlapEast, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
makeAdjQuads(builder, adjDataEast[1], ELodDirection.EAST, maxX, y, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
|
||||
color, adjOverlapEast, skyLightTop, blockLight);
|
||||
color, adjOverlapEast, skyLightTop, blockLight,
|
||||
topData, bottomData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void makeAdjQuads(LodQuadBuilder builder, ColumnArrayView adjData, ELodDirection direction, short x, short y,
|
||||
short z, short w0, short wy, int color, int overlapColor, byte upSkyLight, byte blockLight)
|
||||
private static void makeAdjQuads(
|
||||
LodQuadBuilder builder, ColumnArrayView adjData, ELodDirection direction, short x, short y,
|
||||
short z, short w0, short wy, int color, int overlapColor, byte upSkyLight, byte blockLight,
|
||||
long topData, long bottomData)
|
||||
{
|
||||
color = ColorUtil.applyShade(color, MC.getShade(direction));
|
||||
ColumnArrayView dataPoint = adjData;
|
||||
@@ -160,7 +202,7 @@ public class ColumnBox {
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, (byte) 15, blockLight);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int i;
|
||||
boolean firstFace = true;
|
||||
boolean allAbove = true;
|
||||
@@ -168,137 +210,174 @@ public class ColumnBox {
|
||||
byte nextSkyLight = upSkyLight;
|
||||
boolean isTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
|
||||
boolean lastWasTransparent = false;
|
||||
for (i = 0; i < dataPoint.size() && RenderDataPointUtil.doesDataPointExist(adjData.get(i))
|
||||
&& !RenderDataPointUtil.isVoid(adjData.get(i)); i++) {
|
||||
long adjPoint = adjData.get(i);
|
||||
|
||||
boolean isAdjTransparent = RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
|
||||
|
||||
if (!(!isTransparent && isAdjTransparent && LodRenderer.transparencyEnabled)) {
|
||||
|
||||
|
||||
short height = RenderDataPointUtil.getHeight(adjPoint);
|
||||
short depth = RenderDataPointUtil.getDepth(adjPoint);
|
||||
|
||||
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor) {
|
||||
|
||||
if (lastWasTransparent && !isAdjTransparent) {
|
||||
height = (short) (RenderDataPointUtil.getHeight(adjData.get(i - 1)) - 1);
|
||||
} else if (isAdjTransparent && (i + 1) < adjData.size()) {
|
||||
if (RenderDataPointUtil.getAlpha(adjData.get(i + 1)) == 255) {
|
||||
depth = (short) (height - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the depth of said block is higher than our max Y, continue
|
||||
// Basically: y < maxY <= _____ height
|
||||
// _______&&: y < maxY <= depth
|
||||
if (y + wy <= depth)
|
||||
continue;
|
||||
// Now: depth < maxY
|
||||
allAbove = false;
|
||||
|
||||
if (height < y) {
|
||||
// Basically: _____ height < y < maxY
|
||||
// _______&&: depth ______ < y < maxY
|
||||
if (firstFace) {
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, RenderDataPointUtil.getLightSky(adjPoint),
|
||||
blockLight);
|
||||
} else {
|
||||
// Now: depth < height < y < previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (previousDepth - y), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
previousDepth = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth <= y) { // AND y <= height
|
||||
if (y + wy <= height) {
|
||||
// Basically: ________ y < maxY <= height
|
||||
// _______&&: depth <= y < maxY
|
||||
// The face is inside adj face completely.
|
||||
if (overlapColor != 0) {
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Otherwise: ________ y <= Height < maxY
|
||||
// _______&&: depth <= y _________ < maxY
|
||||
// the adj data intersects the lower part of the current data
|
||||
if (height > y && overlapColor != 0) {
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (height - y), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
// if this is the only face, use the maxY and break,
|
||||
// if there was another face we finish the last one and break
|
||||
if (firstFace) {
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
} else {
|
||||
// Now: depth <= y <= height <= previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousDepth > height) {
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (previousDepth - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
previousDepth = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// In here always true: y < depth < maxY
|
||||
// _________________&&: y < _____ (height and maxY)
|
||||
|
||||
if (y + wy <= height) {
|
||||
// Basically: y _______ < maxY <= height
|
||||
// _______&&: y < depth < maxY
|
||||
// the adj data intersects the higher part of the current data
|
||||
if (overlapColor != 0) {
|
||||
builder.addQuadAdj(direction, x, depth, z, w0, (short) (y + wy - depth), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
// we start the creation of a new face
|
||||
} else {
|
||||
// Otherwise: y < _____ height < maxY
|
||||
// _______&&: y < depth ______ < maxY
|
||||
if (overlapColor != 0) {
|
||||
builder.addQuadAdj(direction, x, depth, z, w0, (short) (height - depth), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
if (firstFace) {
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
} else {
|
||||
// Now: y < depth < height <= previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousDepth > height) {
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (previousDepth - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
previousDepth = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set next top as current depth
|
||||
previousDepth = depth;
|
||||
firstFace = false;
|
||||
nextSkyLight = upSkyLight;
|
||||
if (i + 1 < adjData.size() && RenderDataPointUtil.doesDataPointExist(adjData.get(i + 1)))
|
||||
nextSkyLight = RenderDataPointUtil.getLightSky(adjData.get(i + 1));
|
||||
lastWasTransparent = isAdjTransparent;
|
||||
}
|
||||
}
|
||||
|
||||
if (allAbove) {
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, upSkyLight, blockLight);
|
||||
} else if (previousDepth != -1) {
|
||||
// We need to finish the last quad.
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (previousDepth - y), color, nextSkyLight,
|
||||
blockLight);
|
||||
}
|
||||
|
||||
if (!RenderDataPointUtil.doesDataPointExist(bottomData))
|
||||
{
|
||||
color = ColorUtil.setAlpha(color, 255);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < dataPoint.size() && RenderDataPointUtil.doesDataPointExist(adjData.get(i))
|
||||
&& !RenderDataPointUtil.isVoid(adjData.get(i)); i++)
|
||||
{
|
||||
long adjPoint = adjData.get(i);
|
||||
|
||||
boolean isAdjTransparent = RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
|
||||
|
||||
if (!(!isTransparent && isAdjTransparent && LodRenderer.transparencyEnabled))
|
||||
{
|
||||
short height = RenderDataPointUtil.getHeight(adjPoint);
|
||||
short depth = RenderDataPointUtil.getDepth(adjPoint);
|
||||
|
||||
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
|
||||
{
|
||||
|
||||
if (lastWasTransparent && !isAdjTransparent)
|
||||
{
|
||||
height = (short) (RenderDataPointUtil.getHeight(adjData.get(i - 1)) - 1);
|
||||
}
|
||||
else if (isAdjTransparent && (i + 1) < adjData.size())
|
||||
{
|
||||
if (RenderDataPointUtil.getAlpha(adjData.get(i + 1)) == 255)
|
||||
{
|
||||
depth = (short) (height - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the depth of said block is higher than our max Y, continue
|
||||
// Basically: y < maxY <= _____ height
|
||||
// _______&&: y < maxY <= depth
|
||||
if (y + wy <= depth)
|
||||
continue;
|
||||
// Now: depth < maxY
|
||||
allAbove = false;
|
||||
|
||||
if (height < y)
|
||||
{
|
||||
// Basically: _____ height < y < maxY
|
||||
// _______&&: depth ______ < y < maxY
|
||||
if (firstFace)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, RenderDataPointUtil.getLightSky(adjPoint),
|
||||
blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now: depth < height < y < previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (previousDepth - y), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
previousDepth = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth <= y)
|
||||
{ // AND y <= height
|
||||
if (y + wy <= height)
|
||||
{
|
||||
// Basically: ________ y < maxY <= height
|
||||
// _______&&: depth <= y < maxY
|
||||
// The face is inside adj face completely.
|
||||
if (overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Otherwise: ________ y <= Height < maxY
|
||||
// _______&&: depth <= y _________ < maxY
|
||||
// the adj data intersects the lower part of the current data
|
||||
if (height > y && overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (height - y), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
// if this is the only face, use the maxY and break,
|
||||
// if there was another face we finish the last one and break
|
||||
if (firstFace)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now: depth <= y <= height <= previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousDepth > height)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (previousDepth - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
previousDepth = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// In here always true: y < depth < maxY
|
||||
// _________________&&: y < _____ (height and maxY)
|
||||
|
||||
if (y + wy <= height)
|
||||
{
|
||||
// Basically: y _______ < maxY <= height
|
||||
// _______&&: y < depth < maxY
|
||||
// the adj data intersects the higher part of the current data
|
||||
if (overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, depth, z, w0, (short) (y + wy - depth), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
// we start the creation of a new face
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise: y < _____ height < maxY
|
||||
// _______&&: y < depth ______ < maxY
|
||||
if (overlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, depth, z, w0, (short) (height - depth), overlapColor, (byte) 15, (byte) 15);
|
||||
}
|
||||
if (firstFace)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now: y < depth < height <= previousDepth < maxY
|
||||
if (previousDepth == -1)
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousDepth > height)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, height, z, w0, (short) (previousDepth - height), color,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
previousDepth = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set next top as current depth
|
||||
previousDepth = depth;
|
||||
firstFace = false;
|
||||
nextSkyLight = upSkyLight;
|
||||
if (i + 1 < adjData.size() && RenderDataPointUtil.doesDataPointExist(adjData.get(i + 1)))
|
||||
nextSkyLight = RenderDataPointUtil.getLightSky(adjData.get(i + 1));
|
||||
lastWasTransparent = isAdjTransparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (allAbove)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, y, z, w0, wy, color, upSkyLight, blockLight);
|
||||
}
|
||||
else if (previousDepth != -1)
|
||||
{
|
||||
// We need to finish the last quad.
|
||||
builder.addQuadAdj(direction, x, y, z, w0, (short) (previousDepth - y), color, nextSkyLight,
|
||||
blockLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-29
@@ -13,6 +13,7 @@ import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.pos.DhBlockPos;
|
||||
import com.seibel.lod.core.render.glObject.GLProxy;
|
||||
import com.seibel.lod.core.render.glObject.buffer.GLVertexBuffer;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.RenderDataPointUtil;
|
||||
import com.seibel.lod.core.util.ThreadUtil;
|
||||
import com.seibel.lod.core.util.objects.Reference;
|
||||
@@ -155,28 +156,27 @@ public class ColumnRenderBufferBuilder
|
||||
}
|
||||
});
|
||||
}
|
||||
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource region, ColumnRenderSource[] adjRegions)
|
||||
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, ColumnRenderSource[] adjRegions)
|
||||
{
|
||||
// Variable initialization
|
||||
EDebugMode debugMode = Config.Client.Advanced.Debugging.debugMode.get();
|
||||
|
||||
// TODO pass in which child DhSectionPos should be built to allow generating partial sections so non-full quadTree RenderSections will render
|
||||
byte detailLevel = region.getDataDetail();
|
||||
byte detailLevel = renderSource.getDataDetail();
|
||||
for (int x = 0; x < ColumnRenderSource.SECTION_SIZE; x++)
|
||||
{
|
||||
for (int z = 0; z < ColumnRenderSource.SECTION_SIZE; z++)
|
||||
{
|
||||
UncheckedInterruptedException.throwIfInterrupted();
|
||||
|
||||
ColumnArrayView posData = region.getVerticalDataPointView(x, z);
|
||||
if (posData.size() == 0
|
||||
|| !RenderDataPointUtil.doesDataPointExist(posData.get(0))
|
||||
|| RenderDataPointUtil.isVoid(posData.get(0)))
|
||||
ColumnArrayView columnRenderData = renderSource.getVerticalDataPointView(x, z);
|
||||
if (columnRenderData.size() == 0
|
||||
|| !RenderDataPointUtil.doesDataPointExist(columnRenderData.get(0))
|
||||
|| RenderDataPointUtil.isVoid(columnRenderData.get(0)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ColumnRenderSource.DebugSourceFlag debugSourceFlag = region.debugGetFlag(x, z);
|
||||
ColumnRenderSource.DebugSourceFlag debugSourceFlag = renderSource.debugGetFlag(x, z);
|
||||
|
||||
ColumnArrayView[][] adjData = new ColumnArrayView[4][];
|
||||
// We extract the adj data in the four cardinal direction
|
||||
@@ -186,6 +186,7 @@ public class ColumnRenderBufferBuilder
|
||||
// to avoid having a "darker border" underground
|
||||
// Arrays.fill(adjShadeDisabled, false);
|
||||
|
||||
|
||||
// We check every adj block in each direction
|
||||
|
||||
// If the adj block is rendered in the same region and with same detail
|
||||
@@ -202,24 +203,24 @@ public class ColumnRenderBufferBuilder
|
||||
int zAdj = z + lodDirection.getNormal().z;
|
||||
boolean isCrossRegionBoundary =
|
||||
(xAdj < 0 || xAdj >= ColumnRenderSource.SECTION_SIZE) ||
|
||||
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
|
||||
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
|
||||
|
||||
ColumnRenderSource adjRegion;
|
||||
byte adjDetail;
|
||||
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)
|
||||
{
|
||||
//we compute at which detail that position should be rendered
|
||||
adjRegion = adjRegions[lodDirection.ordinal() - 2];
|
||||
if (adjRegion == null)
|
||||
adjRenderSource = adjRegions[lodDirection.ordinal() - 2];
|
||||
if (adjRenderSource == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
adjDetail = adjRegion.getDataDetail();
|
||||
if (adjDetail != detailLevel)
|
||||
adjDetailLevel = adjRenderSource.getDataDetail();
|
||||
if (adjDetailLevel != detailLevel)
|
||||
{
|
||||
//TODO: Implement this
|
||||
}
|
||||
@@ -240,50 +241,51 @@ public class ColumnRenderBufferBuilder
|
||||
}
|
||||
else
|
||||
{
|
||||
adjRegion = region;
|
||||
adjDetail = detailLevel;
|
||||
adjRenderSource = renderSource;
|
||||
adjDetailLevel = detailLevel;
|
||||
}
|
||||
|
||||
if (adjDetail < detailLevel - 1 || adjDetail > detailLevel + 1)
|
||||
if (adjDetailLevel < detailLevel - 1 || adjDetailLevel > detailLevel + 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (adjDetail == detailLevel || adjDetail > detailLevel)
|
||||
if (adjDetailLevel == detailLevel || adjDetailLevel > detailLevel)
|
||||
{
|
||||
adjData[lodDirection.ordinal() - 2] = new ColumnArrayView[1];
|
||||
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getVerticalDataPointView(xAdj, zAdj);
|
||||
adjData[lodDirection.ordinal() - 2][0] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
|
||||
}
|
||||
else
|
||||
{
|
||||
adjData[lodDirection.ordinal() - 2] = new ColumnArrayView[2];
|
||||
adjData[lodDirection.ordinal() - 2][0] = adjRegion.getVerticalDataPointView(xAdj, zAdj);
|
||||
adjData[lodDirection.ordinal() - 2][1] = adjRegion.getVerticalDataPointView(
|
||||
adjData[lodDirection.ordinal() - 2][0] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
|
||||
adjData[lodDirection.ordinal() - 2][1] = adjRenderSource.getVerticalDataPointView(
|
||||
xAdj + (lodDirection.getAxis() == ELodDirection.Axis.X ? 0 : 1),
|
||||
zAdj + (lodDirection.getAxis() == ELodDirection.Axis.Z ? 0 : 1));
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
EVENT_LOGGER.warn("Failed to get adj data for [{}:{},{}] at [{}]", detailLevel, x, z, lodDirection);
|
||||
EVENT_LOGGER.warn("Failed to get adj data for ["+detailLevel+":"+x+","+z+"] at ["+lodDirection+"]");
|
||||
EVENT_LOGGER.warn("Detail exception: ", e);
|
||||
}
|
||||
} // for adjacent directions
|
||||
|
||||
|
||||
// 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 < posData.size(); i++)
|
||||
for (int i = 0; i < columnRenderData.size(); i++)
|
||||
{
|
||||
long data = posData.get(i);
|
||||
// If the data is not renderable (Void or non-existing) we stop since there is
|
||||
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))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
long adjDataTop = i - 1 >= 0 ? posData.get(i - 1) : RenderDataPointUtil.EMPTY_DATA;
|
||||
long adjDataBot = i + 1 < posData.size() ? posData.get(i + 1) : RenderDataPointUtil.EMPTY_DATA;
|
||||
long adjDataTop = (i - 1) >= 0 ? columnRenderData.get(i - 1) : RenderDataPointUtil.EMPTY_DATA;
|
||||
long adjDataBot = (i + 1) < columnRenderData.size() ? columnRenderData.get(i + 1) : RenderDataPointUtil.EMPTY_DATA;
|
||||
|
||||
CubicLodTemplate.addLodToBuffer(data, adjDataTop, adjDataBot, adjData, detailLevel,
|
||||
x, z, quadBuilder, debugMode, debugSourceFlag);
|
||||
@@ -292,7 +294,7 @@ public class ColumnRenderBufferBuilder
|
||||
}// for z
|
||||
}// for x
|
||||
|
||||
quadBuilder.mergeQuads();
|
||||
quadBuilder.finalizeData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+227
-93
@@ -21,11 +21,9 @@ package com.seibel.lod.core.dataObjects.render.bufferBuilding;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
import com.seibel.lod.core.pos.Pos2D;
|
||||
import com.seibel.lod.core.render.AbstractRenderBuffer;
|
||||
import com.seibel.lod.core.enums.ELodDirection;
|
||||
import com.seibel.lod.core.enums.ELodDirection.Axis;
|
||||
@@ -39,20 +37,28 @@ import org.apache.logging.log4j.Logger;
|
||||
//TODO: Recheck this class for refactoring
|
||||
|
||||
/**
|
||||
* Used to create the quads before they are converted to renderable buffers.
|
||||
*
|
||||
* @version 2022-4-9
|
||||
* Used to create the quads before they are converted to render-able buffers. <br><br>
|
||||
*
|
||||
* Note: the magic number 6 you see throughout this method represents the number of sides on a cube.
|
||||
*/
|
||||
public class LodQuadBuilder
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public final boolean skipQuadsWithZeroSkylight;
|
||||
public final short skyLightCullingBelow;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||
private final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||
@SuppressWarnings("unchecked")
|
||||
final ArrayList<BufferQuad>[] transparentQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||
final boolean doTransparency;
|
||||
private final ArrayList<BufferQuad>[] transparentQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||
private final boolean doTransparency;
|
||||
|
||||
/** Used to turn transparent LODs above the void opaque to prevent seeing the void. */
|
||||
private final HashMap<Pos2D, Short> minOpaqueHeightByRelativePos = new HashMap<>(64*64); // the 64*64 capacity was the smallest James saw the builder work with, so it should be a good starting point
|
||||
/** See {@link LodQuadBuilder#minOpaqueHeightByRelativePos} */
|
||||
private final HashMap<Pos2D, Short> minTransparentHeightByRelativePos = new HashMap<>(64*64);
|
||||
|
||||
|
||||
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
||||
{
|
||||
@@ -107,56 +113,105 @@ public class LodQuadBuilder
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency)
|
||||
{
|
||||
this.doTransparency = doTransparency;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
opaqueQuads[i] = new ArrayList<>();
|
||||
if (doTransparency) transparentQuads[i] = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
this.opaqueQuads[i] = new ArrayList<>();
|
||||
if (doTransparency)
|
||||
{
|
||||
this.transparentQuads[i] = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
this.skipQuadsWithZeroSkylight = enableSkylightCulling;
|
||||
this.skyLightCullingBelow = skyLightCullingBelow;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// add quads //
|
||||
//===========//
|
||||
|
||||
public void addQuadAdj(ELodDirection dir, short x, short y, short z,
|
||||
short widthEastWest, short widthNorthSouthOrUpDown,
|
||||
int color, byte skylight, byte blocklight)
|
||||
int color, byte skyLight, byte blockLight)
|
||||
{
|
||||
if (dir.ordinal() <= ELodDirection.DOWN.ordinal())
|
||||
if (dir == ELodDirection.DOWN)
|
||||
{
|
||||
throw new IllegalArgumentException("addQuadAdj() is only for adj direction! Not UP or Down!");
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y+widthNorthSouthOrUpDown < skyLightCullingBelow)
|
||||
return;
|
||||
BufferQuad quad = new BufferQuad(x, y, z, widthEastWest, widthNorthSouthOrUpDown, color, skylight, blocklight, dir);
|
||||
ArrayList<BufferQuad> qs = (doTransparency && ColorUtil.getAlpha(color) < 255)
|
||||
? transparentQuads[dir.ordinal()] : opaqueQuads[dir.ordinal()];
|
||||
if (!qs.isEmpty() &&
|
||||
(qs.get(qs.size()-1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
||||
|| qs.get(qs.size()-1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
|
||||
) {
|
||||
premergeCount++;
|
||||
}
|
||||
|
||||
if (this.skipQuadsWithZeroSkylight && skyLight == 0 && y + widthNorthSouthOrUpDown < this.skyLightCullingBelow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
qs.add(quad);
|
||||
|
||||
BufferQuad quad = new BufferQuad(x, y, z, widthEastWest, widthNorthSouthOrUpDown, color, 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))
|
||||
)
|
||||
{
|
||||
this.premergeCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
quadList.add(quad);
|
||||
}
|
||||
|
||||
// XZ
|
||||
public void addQuadUp(short x, short y, short z, short width, short wz, int color, byte skylight, byte blocklight)
|
||||
public void addQuadUp(short x, short y, short z, short widthEastWest, short widthNorthSouthOrUpDown, int color, byte skylight, byte blocklight) // TODO argument names are wrong
|
||||
{
|
||||
if (skipQuadsWithZeroSkylight && skylight == 0 && y < skyLightCullingBelow)
|
||||
return;
|
||||
BufferQuad quad = new BufferQuad(x, y, z, width, wz, color, skylight, blocklight, ELodDirection.UP);
|
||||
ArrayList<BufferQuad> qs = (doTransparency && ColorUtil.getAlpha(color) < 255)
|
||||
? transparentQuads[ELodDirection.UP.ordinal()] : opaqueQuads[ELodDirection.UP.ordinal()];
|
||||
if (!qs.isEmpty() &&
|
||||
(qs.get(qs.size()-1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
||||
|| qs.get(qs.size()-1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
|
||||
) {
|
||||
premergeCount++;
|
||||
// cave culling
|
||||
if (this.skipQuadsWithZeroSkylight && skylight == 0 && y < this.skyLightCullingBelow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
qs.add(quad);
|
||||
|
||||
BufferQuad quad = new BufferQuad(x, y, z, widthEastWest, widthNorthSouthOrUpDown, color, skylight, blocklight, ELodDirection.UP);
|
||||
boolean isTransparent = (this.doTransparency && ColorUtil.getAlpha(color) < 255);
|
||||
ArrayList<BufferQuad> quadList = isTransparent ? this.transparentQuads[ELodDirection.UP.ordinal()] : this.opaqueQuads[ELodDirection.UP.ordinal()];
|
||||
|
||||
|
||||
// update the minimum relative height for this quad's positions
|
||||
for (int xRel = x; xRel < (x+widthEastWest); xRel++)
|
||||
{
|
||||
for (int zRel = z; zRel < (z+widthNorthSouthOrUpDown); zRel++)
|
||||
{
|
||||
Pos2D relPos = new Pos2D(xRel,zRel);
|
||||
|
||||
HashMap<Pos2D, Short> minHeightByRelativePos = isTransparent ? this.minTransparentHeightByRelativePos : this.minOpaqueHeightByRelativePos;
|
||||
Short currentHeight = minHeightByRelativePos.get(relPos);
|
||||
// the default height is MAX_VALUE to preserve the comparison logic later
|
||||
currentHeight = (currentHeight == null) ? Short.MAX_VALUE : currentHeight;
|
||||
|
||||
minHeightByRelativePos.put(relPos, (short) Math.min(currentHeight, quad.y));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// attempt to merge this quad with adjacent ones
|
||||
if (!quadList.isEmpty() &&
|
||||
(
|
||||
quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.EastWest)
|
||||
|| quadList.get(quadList.size() - 1).tryMerge(quad, BufferMergeDirectionEnum.NorthSouthOrUpDown))
|
||||
)
|
||||
{
|
||||
this.premergeCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
quadList.add(quad);
|
||||
}
|
||||
|
||||
public void addQuadDown(short x, short y, short z, short width, short wz, int color, byte skylight, byte blocklight)
|
||||
@@ -176,38 +231,11 @@ public class LodQuadBuilder
|
||||
qs.add(quad);
|
||||
}
|
||||
|
||||
private static void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte skylight, byte blocklight, int mx, int my, int mz)
|
||||
{
|
||||
skylight %= 16;
|
||||
blocklight %= 16;
|
||||
|
||||
bb.putShort(x);
|
||||
bb.putShort(y);
|
||||
bb.putShort(z);
|
||||
|
||||
short meta = 0;
|
||||
meta |= (skylight | (blocklight << 4));
|
||||
byte mirco = 0;
|
||||
// mirco offset which is a xyz 2bit value
|
||||
// 0b00 = no offset
|
||||
// 0b01 = positive offset
|
||||
// 0b11 = negative offset
|
||||
// format is: 0b00zzyyxx
|
||||
if (mx != 0) mirco |= mx > 0 ? 0b01 : 0b11;
|
||||
if (my != 0) mirco |= my > 0 ? 0b0100 : 0b1100;
|
||||
if (mz != 0) mirco |= mz > 0 ? 0b010000 : 0b110000;
|
||||
meta |= mirco << 8;
|
||||
|
||||
bb.putShort(meta);
|
||||
byte r = (byte) ColorUtil.getRed(color);
|
||||
byte g = (byte) ColorUtil.getGreen(color);
|
||||
byte b = (byte) ColorUtil.getBlue(color);
|
||||
byte a = (byte) ColorUtil.getAlpha(color);
|
||||
bb.put(r);
|
||||
bb.put(g);
|
||||
bb.put(b);
|
||||
bb.put(a);
|
||||
}
|
||||
|
||||
|
||||
//==============//
|
||||
// add vertices //
|
||||
//==============//
|
||||
|
||||
private static void putQuad(ByteBuffer bb, BufferQuad quad)
|
||||
{
|
||||
@@ -256,28 +284,83 @@ public class LodQuadBuilder
|
||||
}
|
||||
}
|
||||
|
||||
private static void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte skylight, byte blocklight, int mx, int my, int mz)
|
||||
{
|
||||
skylight %= 16;
|
||||
blocklight %= 16;
|
||||
|
||||
bb.putShort(x);
|
||||
bb.putShort(y);
|
||||
bb.putShort(z);
|
||||
|
||||
short meta = 0;
|
||||
meta |= (skylight | (blocklight << 4));
|
||||
byte mirco = 0;
|
||||
// mirco offset which is a xyz 2bit value
|
||||
// 0b00 = no offset
|
||||
// 0b01 = positive offset
|
||||
// 0b11 = negative offset
|
||||
// format is: 0b00zzyyxx
|
||||
if (mx != 0) mirco |= mx > 0 ? 0b01 : 0b11;
|
||||
if (my != 0) mirco |= my > 0 ? 0b0100 : 0b1100;
|
||||
if (mz != 0) mirco |= mz > 0 ? 0b010000 : 0b110000;
|
||||
meta |= mirco << 8;
|
||||
|
||||
bb.putShort(meta);
|
||||
byte r = (byte) ColorUtil.getRed(color);
|
||||
byte g = (byte) ColorUtil.getGreen(color);
|
||||
byte b = (byte) ColorUtil.getBlue(color);
|
||||
byte a = (byte) ColorUtil.getAlpha(color);
|
||||
bb.put(r);
|
||||
bb.put(g);
|
||||
bb.put(b);
|
||||
bb.put(a);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// data finalizing //
|
||||
//=================//
|
||||
|
||||
/** runs any final data cleanup, merging, etc. */
|
||||
public void finalizeData()
|
||||
{
|
||||
this.mergeQuads();
|
||||
this.fixTransparencyOverVoid(); // should happen after merging
|
||||
}
|
||||
|
||||
/** Uses Greedy meshing to merge this builder's Quads. */
|
||||
public void mergeQuads()
|
||||
{
|
||||
long mergeCount = 0;
|
||||
long preQuadsCount = getCurrentOpaqueQuadsCount() + getCurrentTransparentQuadsCount();
|
||||
long preQuadsCount = this.getCurrentOpaqueQuadsCount() + this.getCurrentTransparentQuadsCount();
|
||||
if (preQuadsCount <= 1)
|
||||
{
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (int directionIndex = 0; directionIndex < 6; directionIndex++)
|
||||
{
|
||||
mergeCount += mergeQuadsInternal(opaqueQuads, directionIndex, BufferMergeDirectionEnum.EastWest);
|
||||
if (doTransparency)
|
||||
mergeCount += mergeQuadsInternal(transparentQuads, directionIndex, BufferMergeDirectionEnum.EastWest);
|
||||
mergeCount += mergeQuadsInternal(this.opaqueQuads, directionIndex, BufferMergeDirectionEnum.EastWest);
|
||||
if (this.doTransparency)
|
||||
{
|
||||
mergeCount += mergeQuadsInternal(this.transparentQuads, directionIndex, BufferMergeDirectionEnum.EastWest);
|
||||
}
|
||||
|
||||
|
||||
// only run the second merge if the face is the top or bottom
|
||||
if (directionIndex == ELodDirection.UP.ordinal() || directionIndex == ELodDirection.DOWN.ordinal())
|
||||
{
|
||||
mergeCount += mergeQuadsInternal(opaqueQuads, directionIndex, BufferMergeDirectionEnum.NorthSouthOrUpDown);
|
||||
if (doTransparency)
|
||||
mergeCount += mergeQuadsInternal(transparentQuads, directionIndex, BufferMergeDirectionEnum.NorthSouthOrUpDown);
|
||||
mergeCount += mergeQuadsInternal(this.opaqueQuads, directionIndex, BufferMergeDirectionEnum.NorthSouthOrUpDown);
|
||||
if (this.doTransparency)
|
||||
{
|
||||
mergeCount += mergeQuadsInternal(this.transparentQuads, directionIndex, BufferMergeDirectionEnum.NorthSouthOrUpDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
long postQuadsCount = getCurrentOpaqueQuadsCount() + getCurrentTransparentQuadsCount();
|
||||
|
||||
long postQuadsCount = this.getCurrentOpaqueQuadsCount() + this.getCurrentTransparentQuadsCount();
|
||||
//if (mergeCount != 0)
|
||||
LOGGER.debug("Merged {}/{}({}) quads", mergeCount, preQuadsCount, mergeCount / (double) preQuadsCount);
|
||||
}
|
||||
@@ -314,6 +397,45 @@ public class LodQuadBuilder
|
||||
}
|
||||
|
||||
|
||||
/** makes any transparent LODs that are over the void opaque to prevent seeing the void through them. */
|
||||
public void fixTransparencyOverVoid()
|
||||
{
|
||||
// make transparent LODs opaque if they are over the void
|
||||
ListIterator<BufferQuad> iter = this.transparentQuads[ELodDirection.UP.ordinal()].listIterator();
|
||||
if (iter.hasNext())
|
||||
{
|
||||
BufferQuad currentQuad = iter.next();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
Pos2D relPos = new Pos2D(currentQuad.x, currentQuad.z);
|
||||
|
||||
Short minOpaqueHeight = this.minOpaqueHeightByRelativePos.get(relPos);
|
||||
minOpaqueHeight = (minOpaqueHeight == null) ? Short.MAX_VALUE : minOpaqueHeight;
|
||||
|
||||
Short minTransHeight = this.minTransparentHeightByRelativePos.get(relPos);
|
||||
minTransHeight = (minTransHeight == null) ? Short.MAX_VALUE : minTransHeight;
|
||||
|
||||
|
||||
if (currentQuad.y < minOpaqueHeight && currentQuad.y == minTransHeight)
|
||||
{
|
||||
// transparent quad is at the bottom, make it opaque to prevent seeing through the world
|
||||
currentQuad.color = ColorUtil.setAlpha(currentQuad.color, 255);
|
||||
|
||||
// move the now-opaque quad into the opaque list (if not done the quads may render on top of other transparent quads)
|
||||
iter.remove();
|
||||
this.opaqueQuads[ELodDirection.UP.ordinal()].add(currentQuad);
|
||||
}
|
||||
|
||||
currentQuad = iter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// buffer setup //
|
||||
//==============//
|
||||
|
||||
public Iterator<ByteBuffer> makeOpaqueVertexBuffers()
|
||||
{
|
||||
@@ -380,7 +502,7 @@ public class LodQuadBuilder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public Iterator<ByteBuffer> makeTransparentVertexBuffers()
|
||||
{
|
||||
return new Iterator<ByteBuffer>()
|
||||
@@ -532,7 +654,7 @@ public class LodQuadBuilder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public BufferFiller makeTransparentBufferFiller(EGpuUploadMethod method)
|
||||
{
|
||||
return new BufferFiller()
|
||||
@@ -610,30 +732,41 @@ public class LodQuadBuilder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
public int getCurrentOpaqueQuadsCount()
|
||||
{
|
||||
int i = 0;
|
||||
for (ArrayList<BufferQuad> qs : opaqueQuads)
|
||||
i += qs.size();
|
||||
for (ArrayList<BufferQuad> quadList : this.opaqueQuads)
|
||||
{
|
||||
i += quadList.size();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
public int getCurrentTransparentQuadsCount()
|
||||
{
|
||||
if (!doTransparency) return 0;
|
||||
if (!this.doTransparency)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (ArrayList<BufferQuad> qs : transparentQuads)
|
||||
i += qs.size();
|
||||
for (ArrayList<BufferQuad> quadList : this.transparentQuads)
|
||||
{
|
||||
i += quadList.size();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/** Returns how many Buffers will be needed to render opaque quads in this builder. */
|
||||
public int getCurrentNeededOpaqueVertexBufferCount()
|
||||
{
|
||||
return MathUtil.ceilDiv(getCurrentOpaqueQuadsCount(), AbstractRenderBuffer.MAX_QUADS_PER_BUFFER);
|
||||
}
|
||||
public int getCurrentNeededOpaqueVertexBufferCount() { return MathUtil.ceilDiv(this.getCurrentOpaqueQuadsCount(), AbstractRenderBuffer.MAX_QUADS_PER_BUFFER); }
|
||||
/** Returns how many Buffers will be needed to render transparent quads in this builder. */
|
||||
public int getCurrentNeededTransparentVertexBufferCount()
|
||||
{
|
||||
@@ -644,4 +777,5 @@ public class LodQuadBuilder
|
||||
|
||||
return MathUtil.ceilDiv(this.getCurrentTransparentQuadsCount(), AbstractRenderBuffer.MAX_QUADS_PER_BUFFER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,10 +33,11 @@ public class ColorUtil
|
||||
//________ DH mod color format is: 0xAA RR GG BB
|
||||
//OpenGL RGBA format native order: 0xRR GG BB AA
|
||||
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
|
||||
|
||||
|
||||
public static final int INVISIBLE = rgbToInt(0, 0, 0, 0);
|
||||
|
||||
public static final int BLACK = rgbToInt(0,0,0);
|
||||
public static final int WHITE = rgbToInt(255,255,255);
|
||||
public static final int TRANSPARENT = rgbToInt(0, 0, 0, 0);
|
||||
public static final int RED = rgbToInt(255,0,0);
|
||||
public static final int GREEN = rgbToInt(0,255,0);
|
||||
public static final int BLUE = rgbToInt(0,0,255);
|
||||
|
||||
Reference in New Issue
Block a user