diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/makamys/neodymium/config/Config.java | 5 | ||||
-rw-r--r-- | src/main/java/makamys/neodymium/renderer/ChunkMesh.java | 72 | ||||
-rw-r--r-- | src/main/java/makamys/neodymium/renderer/MeshQuad.java | 323 | ||||
-rw-r--r-- | src/main/java/makamys/neodymium/renderer/NeoRenderer.java | 9 | ||||
-rw-r--r-- | src/main/resources/shaders/chunk.frag | 14 | ||||
-rw-r--r-- | src/main/resources/shaders/chunk.vert | 9 |
6 files changed, 15 insertions, 417 deletions
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<MeshQuad> quads = quadBuf.getAsList(); - - if(Config.simplifyChunkMeshes) { - ArrayList<ArrayList<MeshQuad>> quadsByPlaneDir = new ArrayList<>(); // XY, XZ, YZ - for(int i = 0; i < 3; i++) { - quadsByPlaneDir.add(new ArrayList<MeshQuad>()); - } - 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<MeshQuad> 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<MeshQuad> 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<MeshQuad> 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<MeshQuad> { - - 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"); } diff --git a/src/main/resources/shaders/chunk.frag b/src/main/resources/shaders/chunk.frag index 1066dc5..5cc33c1 100644 --- a/src/main/resources/shaders/chunk.frag +++ b/src/main/resources/shaders/chunk.frag @@ -4,7 +4,6 @@ out vec4 FragColor; in vec2 TexCoord; in vec2 BTexCoord; in vec4 Color; -in vec4 MQPos; in vec4 Viewport; in mat4 ProjInv; in vec4 FogColor; @@ -17,18 +16,7 @@ uniform sampler2D lightTex; void main() { - vec2 goodTexCoord = TexCoord; - -#ifdef SIMPLIFY_MESHES - if(MQPos.x <= 254){ - float wrappedU = mod(MQPos.x, 1.0); - float wrappedV = mod(MQPos.y, 1.0); - - goodTexCoord = ProvokingTexCoord.xy + (((TexCoord.xy - ProvokingTexCoord.xy) / MQPos.zw) * vec2(wrappedU, wrappedV)); - } -#endif - - vec4 texColor = texture(atlas, goodTexCoord + vec4 texColor = texture(atlas, TexCoord #ifdef SHORT_UV / 32768.0 #endif diff --git a/src/main/resources/shaders/chunk.vert b/src/main/resources/shaders/chunk.vert index f983323..f8a42a0 100644 --- a/src/main/resources/shaders/chunk.vert +++ b/src/main/resources/shaders/chunk.vert @@ -4,10 +4,6 @@ layout (location = 1) in vec2 aTexCoord; layout (location = 2) in vec2 aBTexCoord; layout (location = 3) in vec4 aColor; -#ifdef SIMPLIFY_MESHES -layout (location = 4) in vec4 aMQPos; // if the first coordinate is 255, it means: disable megaquad processing for this quad -#endif - uniform mat4 modelView; uniform mat4 proj; uniform mat4 projInv; @@ -22,7 +18,6 @@ uniform vec3 playerPos; out vec2 TexCoord; out vec2 BTexCoord; out vec4 Color; -out vec4 MQPos; out vec4 Viewport; out mat4 ProjInv; out vec4 FogColor; @@ -37,10 +32,6 @@ void main() BTexCoord = aBTexCoord; Color = aColor; -#ifdef SIMPLIFY_MESHES - MQPos = aMQPos; -#endif - Viewport = viewport; ProjInv = projInv; FogColor = fogColor; |