From 6dc520d17d6bc920a4b87e7536678bc11e32b0cd Mon Sep 17 00:00:00 2001 From: FalsePattern Date: Mon, 27 Nov 2023 14:07:21 +0100 Subject: Removed the SimplifyMeshes feature --- src/main/java/makamys/neodymium/config/Config.java | 5 +- .../java/makamys/neodymium/renderer/ChunkMesh.java | 72 +---- .../java/makamys/neodymium/renderer/MeshQuad.java | 323 +-------------------- .../makamys/neodymium/renderer/NeoRenderer.java | 9 - 4 files changed, 14 insertions(+), 395 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/makamys/neodymium/config/Config.java b/src/main/java/makamys/neodymium/config/Config.java index 32d2c22..e644d9b 100644 --- a/src/main/java/makamys/neodymium/config/Config.java +++ b/src/main/java/makamys/neodymium/config/Config.java @@ -44,10 +44,7 @@ public class Config { public static boolean enabled; @ConfigBoolean(cat="_general", def=false, com="Apply changes made in the config file immediately without having to manually reload the renderer. Off by default because it could potentially cause poor performance on certain platforms.") public static boolean hotswap; - - @NeedsReload - @ConfigBoolean(cat="render", def=false, com="Simplify chunk meshes so they are made of less vertices. Reduces vertex count at the cost of increasing shader complexity. It seems to reduce performance overall.") - public static boolean simplifyChunkMeshes; + @ConfigBoolean(cat="render", def=true, com="Don't submit faces for rendering if they are facing away from the camera. Reduces GPU workload at the cost of increasing driver overhead. This will improve the framerate most of the time, but may reduce it if you are not fillrate-limited (such as when playing on a small resolution).") public static boolean cullFaces; @NeedsReload diff --git a/src/main/java/makamys/neodymium/renderer/ChunkMesh.java b/src/main/java/makamys/neodymium/renderer/ChunkMesh.java index 9dd0af0..280772b 100644 --- a/src/main/java/makamys/neodymium/renderer/ChunkMesh.java +++ b/src/main/java/makamys/neodymium/renderer/ChunkMesh.java @@ -151,35 +151,7 @@ public class ChunkMesh extends Mesh { public void finishConstruction() { List quads = quadBuf.getAsList(); - - if(Config.simplifyChunkMeshes) { - ArrayList> quadsByPlaneDir = new ArrayList<>(); // XY, XZ, YZ - for(int i = 0; i < 3; i++) { - quadsByPlaneDir.add(new ArrayList()); - } - for(MeshQuad quad : quads) { - if(quad.getPlane() != MeshQuad.Plane.NONE) { - quadsByPlaneDir.get(quad.getPlane().ordinal() - 1).add(quad); - } - } - for(int plane = 0; plane < 3; plane++) { - quadsByPlaneDir.get(plane).sort(MeshQuad.QuadPlaneComparator.quadPlaneComparators[plane]); - } - - for(int plane = 0; plane < 3; plane++) { - List planeDirQuads = quadsByPlaneDir.get(plane); - int planeStart = 0; - for(int quadI = 0; quadI < planeDirQuads.size(); quadI++) { - MeshQuad quad = planeDirQuads.get(quadI); - MeshQuad nextQuad = quadI == planeDirQuads.size() - 1 ? null : planeDirQuads.get(quadI + 1); - if(!quad.onSamePlaneAs(nextQuad)) { - simplifyPlane(planeDirQuads.subList(planeStart, quadI + 1)); - planeStart = quadI + 1; - } - } - } - } - + quadCount = countValidQuads(quads); buffer = createBuffer(quads, quadCount); usedRAM += buffer.limit(); @@ -187,48 +159,6 @@ public class ChunkMesh extends Mesh { quadBuf.reset(); } - private static void simplifyPlane(List planeQuads) { - // Exclude quads from merging if they have identical vertex positions to another quad. - // Workaround for z-fighting issue that arises when merging fancy grass and the overlay quad - // is a different dimension than the base quad. - for(int i = 0; i < planeQuads.size(); i++) { - MeshQuad a = planeQuads.get(i); - for(int j = i + 1; j < planeQuads.size(); j++) { - MeshQuad b = planeQuads.get(j); - if(!a.noMerge && a.isPosEqual(b)) { - a.noMerge = true; - b.noMerge = true; - } else { - // Due to sorting, identical quads will always be next to each other - break; - } - } - } - - MeshQuad lastQuad = null; - // Pass 1: merge quads to create rows - for(MeshQuad quad : planeQuads) { - if(lastQuad != null) { - lastQuad.tryToMerge(quad); - } - if(MeshQuad.isValid(quad)) { - lastQuad = quad; - } - } - - for(int i = 0; i < planeQuads.size(); i++) { - planeQuads.get(i).mergeReference = null; - } - - // Pass 2: merge rows to create rectangles - // TODO optimize? - for(int i = 0; i < planeQuads.size(); i++) { - for(int j = i + 1; j < planeQuads.size(); j++) { - planeQuads.get(i).tryToMerge(planeQuads.get(j)); - } - } - } - private static int countValidQuads(List quads) { int quadCount = 0; for(MeshQuad quad : quads) { diff --git a/src/main/java/makamys/neodymium/renderer/MeshQuad.java b/src/main/java/makamys/neodymium/renderer/MeshQuad.java index ff2b1fc..94b981c 100644 --- a/src/main/java/makamys/neodymium/renderer/MeshQuad.java +++ b/src/main/java/makamys/neodymium/renderer/MeshQuad.java @@ -12,22 +12,6 @@ import makamys.neodymium.config.Config; import makamys.neodymium.util.BufferWriter; import makamys.neodymium.util.Util; -/* - * This is what a quad looks like. - * - * 0--1 - * | | - * 3--2 - * - * We can glue quads together, forming a megaquad. - * In the fragment shader we need to know which quad of the megaquad we are operating on. - * For this reason, we store the "megaquad X" and "megaquad Y" coordinates in the vertices. - * Their values at vertex 0: (0, 0) - * Their values at vertex 1: (megaquad width, 0) - * Their values at vertex 2: (megaquad width, megaquad height) - * Their values at vertex 3: (0, megaquad height) - */ - public class MeshQuad { private final static int DEFAULT_BRIGHTNESS = Util.createBrightness(15, 15); private final static int DEFAULT_COLOR = 0xFFFFFFFF; @@ -35,38 +19,16 @@ public class MeshQuad { public float[] xs = new float[4]; public float[] ys = new float[4]; public float[] zs = new float[4]; - public float minX = Float.POSITIVE_INFINITY; - public float minY = Float.POSITIVE_INFINITY; - public float minZ = Float.POSITIVE_INFINITY; - public float maxX = Float.NEGATIVE_INFINITY; - public float maxY = Float.NEGATIVE_INFINITY; - public float maxZ = Float.NEGATIVE_INFINITY; public float[] us = new float[4]; public float[] vs = new float[4]; - public int[] bs = new int[4]; public int[] cs = new int[4]; // TODO normals? + public int[] bs = new int[4]; + public boolean deleted; - public boolean noMerge; - + public QuadNormal normal; - public int offset; - public ChunkMesh.Flags flags; - - // Is positive U direction parallel to edge 0-1? - public boolean uDirectionIs01; - - public boolean isRectangle; - - // 0: quads glued together on edge 1-2 or 3-0 ("megaquad row length") - // 1: quads glued together on edge 0-1 or 2-3 ("megaquad column length") - private int[] quadCountByDirection = {1, 1}; - public static int[] totalMergeCountByPlane = new int[3]; - - // When we merge with another quad, we forget what we used to be like. - // Keep a reference to the quad we first merged with, and use it as a reminder. - public MeshQuad mergeReference; - + private static Vector3f vectorA = new Vector3f(); private static Vector3f vectorB = new Vector3f(); private static Vector3f vectorC = new Vector3f(); @@ -82,10 +44,13 @@ public class MeshQuad { us[vi] = Float.intBitsToFloat(rawBuffer[i + 3]); vs[vi] = Float.intBitsToFloat(rawBuffer[i + 4]); - - bs[vi] = flags.hasBrightness ? rawBuffer[i + 7] : DEFAULT_BRIGHTNESS; + cs[vi] = flags.hasColor ? rawBuffer[i + 5] : DEFAULT_COLOR; - + + // TODO normals? + + bs[vi] = flags.hasBrightness ? rawBuffer[i + 7] : DEFAULT_BRIGHTNESS; + i += 8; } @@ -104,8 +69,8 @@ public class MeshQuad { } public void setState(int[] rawBuffer, int offset, ChunkMesh.Flags flags, int drawMode, float offsetX, float offsetY, float offsetZ) { - resetState(); - + deleted = false; + read(rawBuffer, offset, offsetX, offsetY, offsetZ, drawMode, flags); if(xs[0] == xs[1] && xs[1] == xs[2] && xs[2] == xs[3] && ys[0] == ys[1] && ys[1] == ys[2] && ys[2] == ys[3]) { @@ -114,15 +79,6 @@ public class MeshQuad { return; } - uDirectionIs01 = us[0] != us[1]; - - updateMinMaxXYZ(); - updateIsRectangle(); - if(!isRectangle) { - // merging non-rectangles (e.g. Carpenter's Blocks wedge) is buggy, don't do it - noMerge = true; - } - vectorA.set(xs[1] - xs[0], ys[1] - ys[0], zs[1] - zs[0]); vectorB.set(xs[2] - xs[1], ys[2] - ys[1], zs[2] - zs[1]); Vector3f.cross(vectorA, vectorB, vectorC); @@ -130,36 +86,9 @@ public class MeshQuad { normal = QuadNormal.fromVector(vectorC); } - private void resetState() { - Arrays.fill(xs, 0); - Arrays.fill(ys, 0); - Arrays.fill(zs, 0); - Arrays.fill(us, 0); - Arrays.fill(vs, 0); - Arrays.fill(bs, 0); - Arrays.fill(cs, 0); - - minX = Float.POSITIVE_INFINITY; - minY = Float.POSITIVE_INFINITY; - minZ = Float.POSITIVE_INFINITY; - maxX = Float.NEGATIVE_INFINITY; - maxY = Float.NEGATIVE_INFINITY; - maxZ = Float.NEGATIVE_INFINITY; - - deleted = noMerge = false; - normal = null; - offset = 0; - flags = null; - uDirectionIs01 = false; - Arrays.fill(quadCountByDirection, 1); - Arrays.fill(totalMergeCountByPlane, 0); - mergeReference = null; - } - public void writeToBuffer(BufferWriter out) throws IOException { for(int vertexI = 0; vertexI < 4; vertexI++) { int vi = vertexI; - int provokingI = 3; float x = xs[vi]; float y = ys[vi]; @@ -188,250 +117,22 @@ public class MeshQuad { out.writeInt(c); - if(Config.simplifyChunkMeshes) { - if((quadCountByUVDirection(false) == 1 && quadCountByUVDirection(true) == 1)) { - // let the fragment shader know this is not a megaquad - out.writeByte((byte)255); - out.writeByte((byte)255); - out.writeByte((byte)255); - out.writeByte((byte)255); - } else { - out.writeByte(us[vi] == us[provokingI] ? 0 : (byte)quadCountByUVDirection(false)); - out.writeByte(vs[vi] == vs[provokingI] ? 0 : (byte)quadCountByUVDirection(true)); - out.writeByte(us[vi] == us[provokingI] ? (byte)0 : 1); - out.writeByte(vs[vi] == vs[provokingI] ? (byte)0 : 1); - } - } - assert out.position() % getStride() == 0; //System.out.println("[" + vertexI + "] x: " + x + ", y: " + y + " z: " + z + ", u: " + u + ", v: " + v + ", b: " + b + ", c: " + c); } } - public int quadCountByUVDirection(boolean v) { - if(v) { - return quadCountByDirection[uDirectionIs01 ? 0 : 1]; - } else { - return quadCountByDirection[uDirectionIs01 ? 1 : 0]; - } - } - public static int getStride() { return 3 * 4 // XYZ (float) + 2 * (Config.shortUV ? 2 : 4) // UV (float) + 4 // B (int) + 4 // C (int) - + (Config.simplifyChunkMeshes ? 4 : 0) // megaquad XY (byte) ; } - private boolean isTranslatedCopyOf(MeshQuad o, boolean checkValid) { - if((!isValid(this) && checkValid) || !isValid(o) || normal != o.normal) return false; - - if(mergeReference != null) { - return mergeReference.isTranslatedCopyOf(o, false); - } - - for(int i = 1; i < 4; i++) { - double relX = xs[i] - xs[0]; - double relY = ys[i] - ys[0]; - double relZ = zs[i] - zs[0]; - - if(o.xs[i] != o.xs[0] + relX || o.ys[i] != o.ys[0] + relY || o.zs[i] != o.zs[0] + relZ) { - return false; - } - } - - for(int i = 0; i < 4; i++) { - if(us[i] != o.us[i] || vs[i] != o.vs[i] || bs[i] != o.bs[i] || cs[i] != o.cs[i]) { - return false; - } - } - - return true; - } - - public void tryToMerge(MeshQuad o) { - if(noMerge || o.noMerge) return; - - if(isTranslatedCopyOf(o, true)) { - int numVerticesTouching = 0; - boolean[] verticesTouching = new boolean[4]; - for(int i = 0; i < 4; i++) { - for(int j = 0; j < 4; j++) { - if(xs[i] == o.xs[j] && ys[i] == o.ys[j] && zs[i] == o.zs[j]) { - verticesTouching[i] = true; - numVerticesTouching++; - } - } - } - if(numVerticesTouching == 2) { - for(int i = 0; i < 4; i++) { - if(verticesTouching[i]) { - copyVertexFrom(o, i, i); - } - } - - if((verticesTouching[0] && verticesTouching[1]) || (verticesTouching[2] && verticesTouching[3])) { - quadCountByDirection[0] += o.quadCountByDirection[0]; - } - if((verticesTouching[1] && verticesTouching[2]) || (verticesTouching[3] && verticesTouching[0])) { - quadCountByDirection[1] += o.quadCountByDirection[1]; - } - - totalMergeCountByPlane[getPlane().ordinal() - 1]++; - - mergeReference = o; - - o.deleted = true; - } - } - } - - private void copyVertexFrom(MeshQuad o, int src, int dest) { - xs[dest] = o.xs[src]; - ys[dest] = o.ys[src]; - zs[dest] = o.zs[src]; - us[dest] = o.us[src]; - vs[dest] = o.vs[src]; - bs[dest] = o.bs[src]; - cs[dest] = o.cs[src]; - - updateMinMaxXYZ(); // TODO isn't doing this a waste? I should get rid of the min/maxXYZ variables entirely. - } - - private void updateMinMaxXYZ() { - for(int i = 0; i < 4; i++) { - minX = Math.min(minX, xs[i]); - minY = Math.min(minY, ys[i]); - minZ = Math.min(minZ, zs[i]); - maxX = Math.max(maxX, xs[i]); - maxY = Math.max(maxY, ys[i]); - maxZ = Math.max(maxZ, zs[i]); - } - } - - private void updateIsRectangle() { - isRectangle = - vertexExists(minX, minY, minZ) && - vertexExists(minX, minY, maxZ) && - vertexExists(minX, maxY, minZ) && - vertexExists(minX, maxY, maxZ) && - vertexExists(maxX, minY, minZ) && - vertexExists(maxX, minY, maxZ) && - vertexExists(maxX, maxY, minZ) && - vertexExists(maxX, maxY, maxZ); - } - - private boolean vertexExists(float x, float y, float z) { - for(int i = 0; i < 4; i++) { - if(xs[i] == x && ys[i] == y && zs[i] == z) { - return true; - } - } - return false; - } - - // maybe minXYZ and maxXYZ should be arrays instead - public double getMin(int coord) { - return coord == 0 ? minX : coord == 1 ? minY : coord == 2 ? minZ : -1; - } - - public double getMax(int coord) { - return coord == 0 ? maxX : coord == 1 ? maxY : coord == 2 ? maxZ : -1; - } - - public boolean onSamePlaneAs(MeshQuad o) { - return isValid(this) && isValid(o) && getPlane() == o.getPlane() && - ((getPlane() == Plane.XY && minZ == o.minZ) || - (getPlane() == Plane.XZ && minY == o.minY) || - (getPlane() == Plane.YZ && minX == o.minX)); - } - - public Plane getPlane() { - return Plane.fromNormal(normal); - } - public static boolean isValid(MeshQuad q) { return q != null && !q.deleted; } - - public boolean isClockwiseXZ() { - return (xs[1] - xs[0]) * (zs[2] - zs[0]) - (xs[2] - xs[0]) * (zs[1] - zs[0]) < 0; - } - - @Override - public String toString() { - return String.format(Locale.ENGLISH, "%s(%.1f, %.1f, %.1f -- %.1f, %.1f, %.1f)", deleted ? "XXX " : "", minX, minY, minZ, maxX, maxY, maxZ); - //return String.format(Locale.ENGLISH, "%s[(%.1f, %.1f, %.1f), (%.1f, %.1f, %.1f), (%.1f, %.1f, %.1f), (%.1f, %.1f, %.1f)]", deleted ? "XXX " : "", xs[0], ys[0], zs[0], xs[1], ys[1], zs[1], xs[2], ys[2], zs[2], xs[3], ys[3], zs[3]); - } - - public static class QuadPlaneComparator implements Comparator { - - public static final QuadPlaneComparator[] quadPlaneComparators = new QuadPlaneComparator[]{ - new QuadPlaneComparator(2, 1, 0), // PLANE_XY -> ZYX - new QuadPlaneComparator(1, 2, 0), // PLANE_XZ -> YZX - new QuadPlaneComparator(0, 2, 1) // PLANE_YZ -> XZY - }; - - private int c0, c1, c2; - - public QuadPlaneComparator(int firstCoordToCompare, int secondCoordToCompare, int thirdCoordToCompare) { - this.c0 = firstCoordToCompare; - this.c1 = secondCoordToCompare; - this.c2 = thirdCoordToCompare; - } - - @Override - public int compare(MeshQuad a, MeshQuad b) { - if(a.getMin(c0) < b.getMin(c0)) { - return -1; - } else if(a.getMin(c0) > b.getMin(c0)) { - return 1; - } else { - if(a.getMin(c1) < b.getMin(c1)) { - return -1; - } else if(a.getMin(c1) > b.getMin(c1)) { - return 1; - } else { - if(a.getMin(c2) < b.getMin(c2)) { - return -1; - } else if(a.getMin(c2) > b.getMin(c2)) { - return 1; - } else { - return (int)Math.signum(a.offset - b.offset); - } - } - } - } - } - - public static enum Plane { - NONE, - XY, - XZ, - YZ; - - public static Plane fromNormal(QuadNormal normal) { - switch(normal) { - case POSITIVE_X: - case NEGATIVE_X: - return YZ; - case POSITIVE_Y: - case NEGATIVE_Y: - return XZ; - case POSITIVE_Z: - case NEGATIVE_Z: - return XY; - default: - return NONE; - } - } - } - - public boolean isPosEqual(MeshQuad b) { - return Arrays.equals(xs, b.xs) && Arrays.equals(ys, b.ys) && Arrays.equals(zs, b.zs); - } } diff --git a/src/main/java/makamys/neodymium/renderer/NeoRenderer.java b/src/main/java/makamys/neodymium/renderer/NeoRenderer.java index b3564ff..1aa83b6 100644 --- a/src/main/java/makamys/neodymium/renderer/NeoRenderer.java +++ b/src/main/java/makamys/neodymium/renderer/NeoRenderer.java @@ -396,17 +396,11 @@ public class NeoRenderer { int uvEnd = Config.shortUV ? 4 * 4 : 5 * 4; glVertexAttribPointer(2, 2, GL_SHORT, false, stride, uvEnd); glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, false, stride, uvEnd + 1 * 4); - if(Config.simplifyChunkMeshes) { - glVertexAttribPointer(4, 4, GL_UNSIGNED_BYTE, false, stride, uvEnd + 2 * 4); - } glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); - if(Config.simplifyChunkMeshes) { - glEnableVertexAttribArray(4); - } for(int i = 0; i < 2; i++) { piFirst[i] = BufferUtils.createIntBuffer(MAX_MESHES); @@ -427,9 +421,6 @@ public class NeoRenderer { if(hasFog == 1) { defines.add("RENDER_FOG"); } - if(Config.simplifyChunkMeshes) { - defines.add("SIMPLIFY_MESHES"); - } if(Config.shortUV) { defines.add("SHORT_UV"); } -- cgit