Fix transparent LODs over the void

This commit is contained in:
James Seibel
2023-05-15 18:28:14 -05:00
parent 3bed312499
commit 537f7164c7
5 changed files with 590 additions and 362 deletions
@@ -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
@@ -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);
}
}
}
@@ -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();
}
@@ -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);