Skip to content

Commit

Permalink
move quad analysis code out of TranslucentGeometryCollector and fix s…
Browse files Browse the repository at this point in the history
…ome bugs around quad count handling and quad init
  • Loading branch information
douira committed Jan 21, 2025
1 parent 744d769 commit 79bf7ed
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 187 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public BuiltSectionMeshParts createCompactModifiedTranslucentMesh(BuiltSectionMe
}

var totalVertexCount = prevMesh.getVertexCounts()[ModelQuadFacing.UNASSIGNED.ordinal()] +
TranslucentData.vertexCountToQuadCount(updatedQuadIndexes.getAddedQuadCount());
TranslucentData.quadCountToVertexCount(updatedQuadIndexes.getAddedQuadCount());
var vertexStride = this.vertexType.getVertexFormat().getStride();
var mergedBuffer = new NativeBuffer(totalVertexCount * vertexStride);
var quadStride = vertexStride * TranslucentData.VERTICES_PER_QUAD;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import org.apache.commons.lang3.mutable.MutableInt;

public class DefaultFluidRenderer {
// TODO: allow this to be changed by vertex format, WARNING: make sure TranslucentGeometryCollector knows about EPSILON
// TODO: allow this to be changed by vertex format, WARNING: make sure TQuad knows about EPSILON
// TODO: move fluid rendering to a separate render pass and control glPolygonOffset and glDepthFunc to fix this properly
public static final float EPSILON = 0.001f;
private static final float ALIGNED_EQUALS_EPSILON = 0.011f;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke
if (translucentVertexBuffer == null) {
throw new IllegalStateException("Translucent mesh was modified but doesn't exist");
}
meshes.put(DefaultTerrainRenderPasses.TRANSLUCENT, buffers.createCompactModifiedTranslucentMesh(meshes.get(DefaultTerrainRenderPasses.TRANSLUCENT), translucentVertexBuffer, translucentData.getUpdatedQuadIndexes()));
var prevMesh = meshes.get(DefaultTerrainRenderPasses.TRANSLUCENT);
meshes.put(DefaultTerrainRenderPasses.TRANSLUCENT, buffers.createCompactModifiedTranslucentMesh(prevMesh, translucentVertexBuffer, translucentData.getUpdatedQuadIndexes()));
prevMesh.getVertexData().free();

// TODO: after placing and removing some blocks it manages to crash with a segfault somewhere
// are we allocating too much or not freeing it?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,149 +97,35 @@ public TranslucentGeometryCollector(SectionPos sectionPos) {
this.sectionPos = sectionPos;
}

private static final float INV_QUANTIZE_EPSILON = 256f;
private static final float QUANTIZE_EPSILON = 1f / INV_QUANTIZE_EPSILON;

static {
// ensure it fits with the fluid renderer epsilon and that it's a power-of-two
// fraction
var targetEpsilon = DefaultFluidRenderer.EPSILON * 2.1f;
if (QUANTIZE_EPSILON <= targetEpsilon && Integer.bitCount((int) INV_QUANTIZE_EPSILON) == 1) {
throw new RuntimeException("epsilon is invalid: " + QUANTIZE_EPSILON);
}
}

public void appendQuad(ChunkVertexEncoder.Vertex[] vertices, ModelQuadFacing facing, int packedNormal) {
float xSum = 0;
float ySum = 0;
float zSum = 0;

// keep track of distinct vertices to compute the center accurately for
// degenerate quads
float lastX = vertices[3].x;
float lastY = vertices[3].y;
float lastZ = vertices[3].z;
int uniqueVertexes = 0;

float posXExtent = Float.NEGATIVE_INFINITY;
float posYExtent = Float.NEGATIVE_INFINITY;
float posZExtent = Float.NEGATIVE_INFINITY;
float negXExtent = Float.POSITIVE_INFINITY;
float negYExtent = Float.POSITIVE_INFINITY;
float negZExtent = Float.POSITIVE_INFINITY;

for (int i = 0; i < 4; i++) {
float x = vertices[i].x;
float y = vertices[i].y;
float z = vertices[i].z;

posXExtent = Math.max(posXExtent, x);
posYExtent = Math.max(posYExtent, y);
posZExtent = Math.max(posZExtent, z);
negXExtent = Math.min(negXExtent, x);
negYExtent = Math.min(negYExtent, y);
negZExtent = Math.min(negZExtent, z);

if (x != lastX || y != lastY || z != lastZ) {
xSum += x;
ySum += y;
zSum += z;
uniqueVertexes++;
}
if (i != 3) {
lastX = x;
lastY = y;
lastZ = z;
}
}

// shrink quad in non-normal directions to prevent intersections caused by
// epsilon offsets applied by FluidRenderer
if (facing != ModelQuadFacing.POS_X && facing != ModelQuadFacing.NEG_X) {
posXExtent -= QUANTIZE_EPSILON;
negXExtent += QUANTIZE_EPSILON;
if (negXExtent > posXExtent) {
negXExtent = posXExtent;
}
}
if (facing != ModelQuadFacing.POS_Y && facing != ModelQuadFacing.NEG_Y) {
posYExtent -= QUANTIZE_EPSILON;
negYExtent += QUANTIZE_EPSILON;
if (negYExtent > posYExtent) {
negYExtent = posYExtent;
}
}
if (facing != ModelQuadFacing.POS_Z && facing != ModelQuadFacing.NEG_Z) {
posZExtent -= QUANTIZE_EPSILON;
negZExtent += QUANTIZE_EPSILON;
if (negZExtent > posZExtent) {
negZExtent = posZExtent;
}
}

// POS_X, POS_Y, POS_Z, NEG_X, NEG_Y, NEG_Z
float[] extents = new float[] { posXExtent, posYExtent, posZExtent, negXExtent, negYExtent, negZExtent };

int direction = facing.ordinal();
var quadList = this.quadLists[direction];
if (quadList == null) {
quadList = new ReferenceArrayList<>();
this.quadLists[direction] = quadList;
}

Vector3fc center = null;
if (!facing.isAligned() || uniqueVertexes != 4) {
var centerX = xSum / uniqueVertexes;
var centerY = ySum / uniqueVertexes;
var centerZ = zSum / uniqueVertexes;
center = new Vector3f(centerX, centerY, centerZ);
}

// check if we need to store vertex positions for this quad, only necessary if it's unaligned or rotated (yet aligned)
var needsVertexPositions = uniqueVertexes != 4 || !facing.isAligned();
if (!needsVertexPositions) {
for (int i = 0; i < 4; i++) {
var vertex = vertices[i];
if (vertex.x != posYExtent && vertex.x != negYExtent ||
vertex.y != posZExtent && vertex.y != negZExtent ||
vertex.z != posXExtent && vertex.z != negXExtent) {
needsVertexPositions = true;
break;
}
}
}

float[] vertexPositions = null;
if (needsVertexPositions) {
vertexPositions = new float[12];
for (int i = 0, itemIndex = 0; i < 4; i++) {
var vertex = vertices[i];
vertexPositions[itemIndex++] = vertex.x;
vertexPositions[itemIndex++] = vertex.y;
vertexPositions[itemIndex++] = vertex.z;
}
}

if (facing.isAligned()) {
// only update global extents if there are no unaligned quads since this is only
// used for the convex box test which doesn't work with unaligned quads anyway
if (!this.hasUnaligned) {
this.extents[0] = Math.max(this.extents[0], posXExtent);
this.extents[1] = Math.max(this.extents[1], posYExtent);
this.extents[2] = Math.max(this.extents[2], posZExtent);
this.extents[3] = Math.min(this.extents[3], negXExtent);
this.extents[4] = Math.min(this.extents[4], negYExtent);
this.extents[5] = Math.min(this.extents[5], negZExtent);
}

TQuad quad;
if (SPLIT_QUADS) {
quad = FullTQuad.fromAligned(facing, extents, center, vertices);
quad = FullTQuad.fromVertices(vertices, facing, packedNormal);
} else {
quad = RegularTQuad.fromAligned(facing, extents, vertexPositions, center);
quad = RegularTQuad.fromVertices(vertices, facing, packedNormal);
}
quadList.add(quad);

// only update global extents if there are no unaligned quads since this is only
// used for the convex box test which doesn't work with unaligned quads anyway
if (!this.hasUnaligned) {
var quadExtents = quad.getExtents();
for (int i = 0; i < 3; i++) {
this.extents[i] = Math.max(this.extents[i], quadExtents[i]);
}
for (int i = 3; i < 6; i++) {
this.extents[i] = Math.min(this.extents[i], quadExtents[i]);
}
}

var extreme = this.alignedExtremes[direction];
var distance = quad.getAccurateDotProduct();

Expand All @@ -260,9 +146,9 @@ public void appendQuad(ChunkVertexEncoder.Vertex[] vertices, ModelQuadFacing fac

TQuad quad;
if (SPLIT_QUADS) {
quad = FullTQuad.fromUnaligned(facing, extents, center, vertices, packedNormal);
quad = FullTQuad.fromVertices(vertices, facing, packedNormal);
} else {
quad = RegularTQuad.fromUnaligned(facing, extents, vertexPositions, center, packedNormal);
quad = RegularTQuad.fromVertices(vertices, facing, packedNormal);
}
quadList.add(quad);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,17 +526,17 @@ static private BSPNode handleUnsortableBySplitting(BSPWorkspace workspace, IntAr

// split with two quads or three quads depending on the orientation
var insideCount = Integer.bitCount(vertexInsideMap);
FullTQuad outsideQuad = new FullTQuad(insideQuad);
FullTQuad outsideQuad = FullTQuad.splittingCopy(insideQuad);
if (insideCount == 2) {
splitQuadEven(vertexInsideMap, insideQuad, outsideQuad, splitPlane, splitDistance);
} else if (insideCount == 3) {
var secondInsideQuad = splitQuadOdd(vertexInsideMap, insideCount, insideQuad, outsideQuad, splitPlane, splitDistance);

throw new UnsupportedOperationException("Not implemented");
// TODO: Implement this
} else {
var secondOutsideQuad = splitQuadOdd(vertexInsideMap, insideCount, insideQuad, outsideQuad, splitPlane, splitDistance);

throw new UnsupportedOperationException("Not implemented");
// TODO: Implement this
}

inside.add(workspace.updateQuad(insideQuad, candidateIndex));
Expand Down Expand Up @@ -598,6 +598,9 @@ static private void splitQuadEven(int vertexInsideMap, FullTQuad insideQuad, Ful

interpolateAttributes(insideVertex, outsideVertex, targetA, targetB, outsideAmount);
}

insideQuad.updateSplitQuadAfterVertexModification();
outsideQuad.updateSplitQuadAfterVertexModification();
}

static private FullTQuad splitQuadOdd(int vertexInsideMap, int insideCount, FullTQuad insideQuad, FullTQuad outsideQuad, Vector3fc splitPlane, float splitDistance) {
Expand All @@ -614,17 +617,6 @@ static private FullTQuad splitQuadOdd(int vertexInsideMap, int insideCount, Full
return null;
}

static private void copyVertexTo(ChunkVertexEncoder.Vertex from, ChunkVertexEncoder.Vertex to) {
to.x = from.x;
to.y = from.y;
to.z = from.z;
to.color = from.color;
to.ao = from.ao;
to.u = from.u;
to.v = from.v;
to.light = from.light;
}

private static void interpolateAttributes(ChunkVertexEncoder.Vertex inside, ChunkVertexEncoder.Vertex outside, ChunkVertexEncoder.Vertex targetA, ChunkVertexEncoder.Vertex targetB, float outsideAmount) {
var newColor = ColorMixer.mix(inside.color, outside.color, outsideAmount);
targetA.color = newColor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,54 @@
import org.joml.Vector3fc;

public class FullTQuad extends RegularTQuad {
private final ChunkVertexEncoder.Vertex[] vertices;
private final ChunkVertexEncoder.Vertex[] vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
private boolean normalIsVeryAccurate = false;

// TODO: when vertexes are modified we need to re-calculate at least the extent, centroid, invalidate vertex positions (but not the normal or dot product)
FullTQuad(ModelQuadFacing facing, float[] extents, Vector3fc center, ChunkVertexEncoder.Vertex[] vertices, int packedNormal) {
super(facing, extents, null, center, packedNormal);
FullTQuad(ModelQuadFacing facing, int packedNormal) {
super(facing, packedNormal);
}

public static FullTQuad fromVertices(ChunkVertexEncoder.Vertex[] vertices, ModelQuadFacing facing, int packedNormal) {
var quad = new FullTQuad(facing, packedNormal);
quad.initFull(vertices);
quad.initVertices(vertices);

return quad;
}

private void initVertices(ChunkVertexEncoder.Vertex[] vertices) {
// deep copy the vertices since the caller may modify them
this.vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
for (int i = 0; i < 4; i++) {
var newVertex = this.vertices[i];
var oldVertex = vertices[i];
newVertex.x = oldVertex.x;
newVertex.y = oldVertex.y;
newVertex.z = oldVertex.z;
newVertex.color = oldVertex.color;
newVertex.ao = oldVertex.ao;
newVertex.u = oldVertex.u;
newVertex.v = oldVertex.v;
newVertex.light = oldVertex.light;
ChunkVertexEncoder.Vertex.copyVertexTo(oldVertex, newVertex);
}
}

public FullTQuad(FullTQuad other) {
this(other.facing, other.extents, other.center, other.vertices, other.packedNormal);
this.normalIsVeryAccurate = other.normalIsVeryAccurate;
public static FullTQuad splittingCopy(FullTQuad quad) {
var newQuad = new FullTQuad(quad.facing, quad.packedNormal);
newQuad.initVertices(quad.vertices);

newQuad.extents = quad.extents;
newQuad.accurateDotProduct = quad.accurateDotProduct;
newQuad.quantizedDotProduct = quad.quantizedDotProduct;

newQuad.center = quad.center;
newQuad.quantizedNormal = quad.quantizedNormal;
newQuad.accurateNormal = quad.accurateNormal;

newQuad.normalIsVeryAccurate = quad.normalIsVeryAccurate;

return newQuad;
}

public void updateSplitQuadAfterVertexModification() {
this.initExtentsAndCenter(this.vertices);

// invalidate vertex positions after modification of the vertices
this.vertexPositions = null;

// no need to update dot product since splitting a quad doesn't change its normal or dot product
}

@Override
Expand Down Expand Up @@ -101,12 +123,4 @@ public Vector3fc getVeryAccurateNormal() {
public ChunkVertexEncoder.Vertex[] getVertices() {
return this.vertices;
}

public static TQuad fromAligned(ModelQuadFacing facing, float[] extents, Vector3fc center, ChunkVertexEncoder.Vertex[] vertices) {
return new FullTQuad(facing, extents, center, vertices, ModelQuadFacing.PACKED_ALIGNED_NORMALS[facing.ordinal()]);
}

public static TQuad fromUnaligned(ModelQuadFacing facing, float[] extents, Vector3fc center, ChunkVertexEncoder.Vertex[] vertices, int packedNormal) {
return new FullTQuad(facing, extents, center, vertices, packedNormal);
}
}
Loading

0 comments on commit 79bf7ed

Please sign in to comment.