Skip to content

Commit

Permalink
Fix sorting failures on rotated cuboids (#2812)
Browse files Browse the repository at this point in the history
Use the accurate vertex positions for unaligned and aligned (but rotated) quads.
  • Loading branch information
douira authored Oct 26, 2024
1 parent d3aed21 commit 65068c5
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,42 @@ public class TQuad {

private ModelQuadFacing facing;
private final float[] extents;
private float[] vertexPositions;
private final int packedNormal;
private float dotProduct;
private final float accurateDotProduct;
private float quantizedDotProduct;
private Vector3fc center; // null on aligned quads
private Vector3fc quantizedNormal;
private Vector3fc accurateNormal;

private TQuad(ModelQuadFacing facing, float[] extents, Vector3fc center, int packedNormal) {
private TQuad(ModelQuadFacing facing, float[] extents, float[] vertexPositions, Vector3fc center, int packedNormal) {
this.facing = facing;
this.extents = extents;
this.vertexPositions = vertexPositions;
this.center = center;
this.packedNormal = packedNormal;

if (this.facing.isAligned()) {
this.dotProduct = getAlignedDotProduct(this.facing, this.extents);
this.accurateDotProduct = getAlignedDotProduct(this.facing, this.extents);
} else {
float normX = NormI8.unpackX(this.packedNormal);
float normY = NormI8.unpackY(this.packedNormal);
float normZ = NormI8.unpackZ(this.packedNormal);
this.dotProduct = this.getCenter().dot(normX, normY, normZ);
this.accurateDotProduct = this.getCenter().dot(normX, normY, normZ);
}
this.quantizedDotProduct = this.accurateDotProduct;
}

private static float getAlignedDotProduct(ModelQuadFacing facing, float[] extents) {
return extents[facing.ordinal()] * facing.getSign();
}

static TQuad fromAligned(ModelQuadFacing facing, float[] extents, Vector3fc center) {
return new TQuad(facing, extents, center, ModelQuadFacing.PACKED_ALIGNED_NORMALS[facing.ordinal()]);
static TQuad fromAligned(ModelQuadFacing facing, float[] extents, float[] vertexPositions, Vector3fc center) {
return new TQuad(facing, extents, vertexPositions, center, ModelQuadFacing.PACKED_ALIGNED_NORMALS[facing.ordinal()]);
}

static TQuad fromUnaligned(ModelQuadFacing facing, float[] extents, Vector3fc center, int packedNormal) {
return new TQuad(facing, extents, center, packedNormal);
static TQuad fromUnaligned(ModelQuadFacing facing, float[] extents, float[] vertexPositions, Vector3fc center, int packedNormal) {
return new TQuad(facing, extents, vertexPositions, center, packedNormal);
}

public ModelQuadFacing getFacing() {
Expand All @@ -73,9 +78,9 @@ public ModelQuadFacing useQuantizedFacing() {
this.getQuantizedNormal();
this.facing = ModelQuadFacing.fromNormal(this.quantizedNormal.x(), this.quantizedNormal.y(), this.quantizedNormal.z());
if (this.facing.isAligned()) {
this.dotProduct = getAlignedDotProduct(this.facing, this.extents);
this.quantizedDotProduct = getAlignedDotProduct(this.facing, this.extents);
} else {
this.dotProduct = this.getCenter().dot(this.quantizedNormal);
this.quantizedDotProduct = this.getCenter().dot(this.quantizedNormal);
}
}

Expand All @@ -86,6 +91,31 @@ public float[] getExtents() {
return this.extents;
}

public float[] getVertexPositions() {
// calculate vertex positions from extents if there's no cached value
// (we don't want to be preemptively collecting vertex positions for all aligned quads)
if (this.vertexPositions == null) {
this.vertexPositions = new float[12];

var facingAxis = this.facing.getAxis();
var xRange = facingAxis == 0 ? 0 : 3;
var yRange = facingAxis == 1 ? 0 : 3;
var zRange = facingAxis == 2 ? 0 : 3;

var itemIndex = 0;
for (int x = 0; x <= xRange; x += 3) {
for (int y = 0; y <= yRange; y += 3) {
for (int z = 0; z <= zRange; z += 3) {
this.vertexPositions[itemIndex++] = this.extents[x];
this.vertexPositions[itemIndex++] = this.extents[y + 1];
this.vertexPositions[itemIndex++] = this.extents[z + 2];
}
}
}
}
return this.vertexPositions;
}

public Vector3fc getCenter() {
// calculate aligned quad center on demand
if (this.center == null) {
Expand All @@ -97,8 +127,12 @@ public Vector3fc getCenter() {
return this.center;
}

public float getDotProduct() {
return this.dotProduct;
public float getAccurateDotProduct() {
return this.accurateDotProduct;
}

public float getQuantizedDotProduct() {
return this.quantizedDotProduct;
}

public int getPackedNormal() {
Expand All @@ -116,6 +150,20 @@ public Vector3fc getQuantizedNormal() {
return this.quantizedNormal;
}

public Vector3fc getAccurateNormal() {
if (this.facing.isAligned()) {
return this.facing.getAlignedNormal();
} else {
if (this.accurateNormal == null) {
this.accurateNormal = new Vector3f(
NormI8.unpackX(this.packedNormal),
NormI8.unpackY(this.packedNormal),
NormI8.unpackZ(this.packedNormal));
}
return this.accurateNormal;
}
}

private void computeQuantizedNormal() {
float normX = NormI8.unpackX(this.packedNormal);
float normY = NormI8.unpackY(this.packedNormal);
Expand Down Expand Up @@ -150,7 +198,7 @@ int getQuadHash() {
} else {
result = 31 * result + this.packedNormal;
}
result = 31 * result + Float.hashCode(this.dotProduct);
result = 31 * result + Float.hashCode(this.quantizedDotProduct);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M
float lastX = vertices[3].x;
float lastY = vertices[3].y;
float lastZ = vertices[3].z;
int uniqueQuads = 0;
int uniqueVertexes = 0;

float posXExtent = Float.NEGATIVE_INFINITY;
float posYExtent = Float.NEGATIVE_INFINITY;
Expand All @@ -141,7 +141,7 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M
xSum += x;
ySum += y;
zSum += z;
uniqueQuads++;
uniqueVertexes++;
}
if (i != 3) {
lastX = x;
Expand Down Expand Up @@ -185,13 +185,38 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M
}

Vector3fc center = null;
if (!facing.isAligned() || uniqueQuads != 4) {
var centerX = xSum / uniqueQuads;
var centerY = ySum / uniqueQuads;
var centerZ = zSum / uniqueQuads;
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
Expand All @@ -204,11 +229,11 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M
this.extents[5] = Math.min(this.extents[5], negZExtent);
}

var quad = TQuad.fromAligned(facing, extents, center);
var quad = TQuad.fromAligned(facing, extents, vertexPositions, center);
quadList.add(quad);

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

// check if this is a new dot product for this distance
var existingExtreme = this.alignedExtremes[direction];
Expand All @@ -225,11 +250,11 @@ public void appendQuad(int packedNormal, ChunkVertexEncoder.Vertex[] vertices, M
} else {
this.hasUnaligned = true;

var quad = TQuad.fromUnaligned(facing, extents, center, packedNormal);
var quad = TQuad.fromUnaligned(facing, extents, vertexPositions, center, packedNormal);
quadList.add(quad);

// update the two unaligned normals that are tracked
var distance = quad.getDotProduct();
var distance = quad.getAccurateDotProduct();
if (packedNormal == this.unalignedANormal) {
if (Float.isNaN(this.unalignedADistance1)) {
this.unalignedADistance1 = distance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private static boolean doubleLeafPossible(TQuad quadA, TQuad quadB) {
// opposite normal (distance irrelevant)
if (NormI8.isOpposite(packedNormalA, packedNormalB)
// same normal and same distance
|| packedNormalA == packedNormalB && quadA.getDotProduct() == quadB.getDotProduct()) {
|| packedNormalA == packedNormalB && quadA.getAccurateDotProduct() == quadB.getAccurateDotProduct()) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ static private BSPNode buildSNRLeafNodeFromQuads(BSPWorkspace workspace, IntArra

for (int i = 0; i < indexes.size(); i++) {
var quadIndex = indexes.getInt(i);
keys[i] = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getDotProduct());
keys[i] = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getAccurateDotProduct());
}

quadIndexes = RadixSort.sort(keys);
Expand All @@ -553,7 +553,7 @@ static private BSPNode buildSNRLeafNodeFromQuads(BSPWorkspace workspace, IntArra

for (int i = 0; i < indexes.size(); i++) {
var quadIndex = indexes.getInt(i);
int dotProductComponent = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getDotProduct());
int dotProductComponent = MathUtil.floatToComparableInt(workspace.quads[quadIndex].getAccurateDotProduct());
sortData[i] = (long) dotProductComponent << 32 | quadIndex;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data;

import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.TQuad;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.trigger.GeometryPlanes;
import net.caffeinemc.mods.sodium.client.util.sorting.RadixSort;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data;

import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortType;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.TQuad;
import net.caffeinemc.mods.sodium.client.util.MathUtil;
Expand Down Expand Up @@ -50,7 +49,7 @@ private static StaticNormalRelativeData fromDoubleUnaligned(int[] vertexCounts,
final var keys = new int[quads.length];

for (int q = 0; q < quads.length; q++) {
keys[q] = MathUtil.floatToComparableInt(quads[q].getDotProduct());
keys[q] = MathUtil.floatToComparableInt(quads[q].getAccurateDotProduct());
}

var indices = RadixSort.sort(keys);
Expand All @@ -62,7 +61,7 @@ private static StaticNormalRelativeData fromDoubleUnaligned(int[] vertexCounts,
final var sortData = new long[quads.length];

for (int q = 0; q < quads.length; q++) {
int dotProductComponent = MathUtil.floatToComparableInt(quads[q].getDotProduct());
int dotProductComponent = MathUtil.floatToComparableInt(quads[q].getAccurateDotProduct());
sortData[q] = (long) dotProductComponent << 32 | q;
}

Expand Down Expand Up @@ -116,7 +115,7 @@ private static StaticNormalRelativeData fromMixed(int[] vertexCounts,
final var keys = new int[count];

for (int q = 0; q < count; q++) {
keys[q] = MathUtil.floatToComparableInt(quads[quadIndex++].getDotProduct());
keys[q] = MathUtil.floatToComparableInt(quads[quadIndex++].getAccurateDotProduct());
}

var indices = RadixSort.sort(keys);
Expand All @@ -127,7 +126,7 @@ private static StaticNormalRelativeData fromMixed(int[] vertexCounts,
} else {
for (int i = 0; i < count; i++) {
var quad = quads[quadIndex++];
int dotProductComponent = MathUtil.floatToComparableInt(quad.getDotProduct());
int dotProductComponent = MathUtil.floatToComparableInt(quad.getAccurateDotProduct());
sortData[i] = (long) dotProductComponent << 32 | i;
}

Expand Down
Loading

0 comments on commit 65068c5

Please sign in to comment.