From 2f4979260b60f90ef0fce1b9305b74bcd9fb8af5 Mon Sep 17 00:00:00 2001 From: makamys Date: Sat, 2 Dec 2023 17:59:34 +0100 Subject: Fix camera shake at high distances - Made chunk meshes use region-relative coordinates - Moved mesh tracking to NeoRegion - Regions are now drawn separately, allowing a different translation to be set for each --- .../java/makamys/neodymium/renderer/ChunkMesh.java | 2 +- .../makamys/neodymium/renderer/Comparators.java | 26 +++++++++ src/main/java/makamys/neodymium/renderer/Mesh.java | 1 + .../java/makamys/neodymium/renderer/NeoRegion.java | 46 +++++++++++++-- .../makamys/neodymium/renderer/NeoRenderer.java | 66 +++++++++++++++------- src/main/java/makamys/neodymium/util/Util.java | 6 ++ src/main/resources/shaders/chunk.vert | 8 +-- 7 files changed, 125 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/main/java/makamys/neodymium/renderer/ChunkMesh.java b/src/main/java/makamys/neodymium/renderer/ChunkMesh.java index 5bd38f6..24acc30 100644 --- a/src/main/java/makamys/neodymium/renderer/ChunkMesh.java +++ b/src/main/java/makamys/neodymium/renderer/ChunkMesh.java @@ -114,7 +114,7 @@ public class ChunkMesh extends Mesh { for(int quadI = 0; quadI < t.vertexCount / verticesPerPrimitive; quadI++) { MeshQuad quad = quadBuf.next(); - quad.setState(t.rawBuffer, tessellatorVertexSize, quadI * (verticesPerPrimitive * tessellatorVertexSize), FLAGS, t.drawMode, (float)-t.xOffset, (float)-t.yOffset, (float)-t.zOffset); + quad.setState(t.rawBuffer, tessellatorVertexSize, quadI * (verticesPerPrimitive * tessellatorVertexSize), FLAGS, t.drawMode, NeoRegion.toRelativeOffset(-t.xOffset), NeoRegion.toRelativeOffset(-t.yOffset), NeoRegion.toRelativeOffset(-t.zOffset)); if(quad.deleted) { quadBuf.remove(); } diff --git a/src/main/java/makamys/neodymium/renderer/Comparators.java b/src/main/java/makamys/neodymium/renderer/Comparators.java index f72cad9..0863b17 100644 --- a/src/main/java/makamys/neodymium/renderer/Comparators.java +++ b/src/main/java/makamys/neodymium/renderer/Comparators.java @@ -4,6 +4,7 @@ import java.util.Comparator; public class Comparators { public static final MeshDistanceComparator MESH_DISTANCE_COMPARATOR = new MeshDistanceComparator(); + public static final RegionDistanceComparator REGION_DISTANCE_COMPARATOR = new RegionDistanceComparator(); public static class MeshDistanceComparator implements Comparator { double x, y, z; @@ -44,4 +45,29 @@ public class Comparators { } } + + public static class RegionDistanceComparator implements Comparator { + double x, y, z; + + public RegionDistanceComparator setOrigin(double x, double y, double z) { + this.x = x / (NeoRegion.SIZE * 16.0); + this.y = y / (NeoRegion.SIZE * 16.0); + this.z = z / (NeoRegion.SIZE * 16.0); + return this; + } + + @Override + public int compare(NeoRegion a, NeoRegion b) { + double distSqA = a.distSq(x, y, z); + double distSqB = b.distSq(x, y, z); + + if(distSqA > distSqB) { + return 1; + } else if(distSqA < distSqB) { + return -1; + } else { + return 0; + } + } + } } diff --git a/src/main/java/makamys/neodymium/renderer/Mesh.java b/src/main/java/makamys/neodymium/renderer/Mesh.java index 6c70523..1fe5003 100644 --- a/src/main/java/makamys/neodymium/renderer/Mesh.java +++ b/src/main/java/makamys/neodymium/renderer/Mesh.java @@ -18,6 +18,7 @@ public abstract class Mesh { public int pass; int x, y, z; public QuadNormal normal = QuadNormal.NONE; + public NeoRegion containingRegion; public double distSq(double x2, double y2, double z2) { return Util.distSq(x + 0.5, y + 0.5, z + 0.5, x2, y2, z2); diff --git a/src/main/java/makamys/neodymium/renderer/NeoRegion.java b/src/main/java/makamys/neodymium/renderer/NeoRegion.java index 36f5909..7e85138 100644 --- a/src/main/java/makamys/neodymium/renderer/NeoRegion.java +++ b/src/main/java/makamys/neodymium/renderer/NeoRegion.java @@ -1,11 +1,19 @@ package makamys.neodymium.renderer; +import java.util.ArrayList; +import java.util.List; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import makamys.neodymium.util.Util; import net.minecraft.entity.Entity; public class NeoRegion { - public static final int SIZE = 32; + public static final int SIZE = 64; private NeoChunk[][] data = new NeoChunk[SIZE][SIZE]; + @Getter + private RenderData renderData; int regionX, regionZ; @@ -16,6 +24,7 @@ public class NeoRegion { public NeoRegion(int regionX, int regionZ) { this.regionX = regionX; this.regionZ = regionZ; + this.renderData = new RenderData(regionX * SIZE * 16, 0, regionZ * SIZE * 16); for(int i = 0; i < SIZE; i++) { for(int j = 0; j < SIZE; j++) { @@ -76,13 +85,42 @@ public class NeoRegion { } + public double distSq(double x, double y, double z) { + return Util.distSq(regionX + 0.5, 0, regionZ + 0.5, x, y, z); + } + + public static float toRelativeOffset(double d) { + return (float)(d - (Math.floor(d / (SIZE * 16.0)) * SIZE * 16.0)); + } + @Override public String toString() { return "LODRegion(" + regionX + ", " + regionZ + ")"; } - public boolean shouldDelete() { - return emptyTicks > 100; + public boolean shouldDelete() { + return emptyTicks > 100; + } + + @RequiredArgsConstructor + public static class RenderData { + public final double originX, originY, originZ; + + private List[] sentMeshes = (List[])new ArrayList[] {new ArrayList(), new ArrayList()}; + public int[] batchLimit = new int[2]; + public int[] batchFirst = new int[2]; + + public void sort(double eyePosX, double eyePosY, double eyePosZ, boolean pass0, boolean pass1) { + if(pass0) { + sentMeshes[0].sort(Comparators.MESH_DISTANCE_COMPARATOR.setOrigin(eyePosX, eyePosY, eyePosZ).setInverted(false)); + } + if(pass1) { + sentMeshes[1].sort(Comparators.MESH_DISTANCE_COMPARATOR.setOrigin(eyePosX, eyePosY, eyePosZ).setInverted(true)); + } + } + + public List getSentMeshes(int i) { + return sentMeshes[i]; + } } - } diff --git a/src/main/java/makamys/neodymium/renderer/NeoRenderer.java b/src/main/java/makamys/neodymium/renderer/NeoRenderer.java index 1caf301..6c0b015 100644 --- a/src/main/java/makamys/neodymium/renderer/NeoRenderer.java +++ b/src/main/java/makamys/neodymium/renderer/NeoRenderer.java @@ -12,14 +12,11 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import makamys.neodymium.Compat; import makamys.neodymium.renderer.attribs.AttributeSet; @@ -72,11 +69,11 @@ public class NeoRenderer { private int[] shaderProgramsNoFog = {0, 0}; private IntBuffer[] piFirst = new IntBuffer[2]; private IntBuffer[] piCount = new IntBuffer[2]; - private List[] sentMeshes = (List[])new ArrayList[] {new ArrayList(), new ArrayList()}; GPUMemoryManager mem; private AttributeSet attributes; private Map loadedRegionsMap = new HashMap<>(); + private List loadedRegionsList = new ArrayList<>(); public World world; @@ -181,27 +178,34 @@ public class NeoRenderer { } private void sort(boolean pass0, boolean pass1) { - if(pass0) { - sentMeshes[0].sort(Comparators.MESH_DISTANCE_COMPARATOR.setOrigin(eyePosX, eyePosY, eyePosZ).setInverted(false)); - } - if(pass1) { - sentMeshes[1].sort(Comparators.MESH_DISTANCE_COMPARATOR.setOrigin(eyePosX, eyePosY, eyePosZ).setInverted(true)); + for(NeoRegion r : loadedRegionsMap.values()) { + r.getRenderData().sort(eyePosX, eyePosY, eyePosZ, pass0, pass1); } } private void initIndexBuffers() { + loadedRegionsList.clear(); + loadedRegionsList.addAll(loadedRegionsMap.values()); + loadedRegionsList.sort(Comparators.REGION_DISTANCE_COMPARATOR.setOrigin(eyePosX, eyePosY, eyePosZ)); + for(int i = 0; i < 2; i++) { piFirst[i].limit(MAX_MESHES); piCount[i].limit(MAX_MESHES); - for(Mesh mesh : sentMeshes[i]) { - WorldRenderer wr = ((ChunkMesh)mesh).wr; - if(mesh.visible && wr.isVisible && shouldRenderMesh(mesh)) { - int meshes = mesh.writeToIndexBuffer(piFirst[i], piCount[i], eyePosXTDiv, eyePosYTDiv, eyePosZTDiv, i); - renderedMeshes += meshes; - for(int j = piCount[i].position() - meshes; j < piCount[i].position(); j++) { - renderedQuads += piCount[i].get(j) / 4; + int order = i == 0 ? 1 : -1; + for(int regionI = order == 1 ? 0 : loadedRegionsList.size() - 1; regionI >= 0 && regionI < loadedRegionsList.size(); regionI += order) { + NeoRegion.RenderData region = loadedRegionsList.get(regionI).getRenderData(); + region.batchFirst[i] = piFirst[i].position(); + for(Mesh mesh : region.getSentMeshes(i)) { + WorldRenderer wr = ((ChunkMesh)mesh).wr; + if(mesh.visible && wr.isVisible && shouldRenderMesh(mesh)) { + int meshes = mesh.writeToIndexBuffer(piFirst[i], piCount[i], eyePosXTDiv, eyePosYTDiv, eyePosZTDiv, i); + renderedMeshes += meshes; + for(int j = piCount[i].position() - meshes; j < piCount[i].position(); j++) { + renderedQuads += piCount[i].get(j) / 4; + } } } + region.batchLimit[i] = piFirst[i].position(); } piFirst[i].flip(); piCount[i].flip(); @@ -296,7 +300,23 @@ public class NeoRenderer { if(isWireframeEnabled()) { GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); } - glMultiDrawArrays(GL_QUADS, piFirst[pass], piCount[pass]); + + int u_renderOffset = glGetUniformLocation(getShaderProgram(pass), "renderOffset"); + + int oldLimit = piFirst[pass].limit(); + + int order = pass == 0 ? 1 : -1; + for(int regionI = order == 1 ? 0 : loadedRegionsList.size() - 1; regionI >= 0 && regionI < loadedRegionsList.size(); regionI += order) { + NeoRegion.RenderData region = loadedRegionsList.get(regionI).getRenderData(); + Util.setPositionAndLimit(piFirst[pass], region.batchFirst[pass], region.batchLimit[pass]); + Util.setPositionAndLimit(piCount[pass], region.batchFirst[pass], region.batchLimit[pass]); + + glUniform3f(u_renderOffset, (float)(region.originX - eyePosX), (float)(region.originY - eyePosY), (float)(region.originZ - eyePosZ)); + glMultiDrawArrays(GL_QUADS, piFirst[pass], piCount[pass]); + } + Util.setPositionAndLimit(piFirst[pass], 0, oldLimit); + Util.setPositionAndLimit(piCount[pass], 0, oldLimit); + if(isWireframeEnabled()) { GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); } @@ -363,7 +383,7 @@ public class NeoRenderer { glUniform2(u_fogStartEnd, fogStartEnd); glUniform1i(u_fogMode, glGetInteger(GL_FOG_MODE)); glUniform1f(u_fogDensity, glGetFloat(GL_FOG_DENSITY)); - + glUniform3f(u_playerPos, (float)eyePosX, (float)eyePosY, (float)eyePosZ); if (Compat.RPLE()) { @@ -606,7 +626,9 @@ public class NeoRenderer { if(mesh.gpuStatus == GPUStatus.UNSENT) { mem.sendMeshToGPU(mesh); - sentMeshes[mesh.pass].add(mesh); + NeoRegion region = getRegionContaining(mesh.x, mesh.z); + region.getRenderData().getSentMeshes(mesh.pass).add(mesh); + mesh.containingRegion = region; } } } @@ -615,7 +637,9 @@ public class NeoRenderer { if(mesh == null) return; mem.deleteMeshFromGPU(mesh); - sentMeshes[mesh.pass].remove(mesh); + if(mesh.containingRegion != null) { + mesh.containingRegion.getRenderData().getSentMeshes(mesh.pass).remove(mesh); + } setMeshVisible(mesh, false); } @@ -669,4 +693,4 @@ public class NeoRenderer { public static enum WorldRendererChange { VISIBLE, INVISIBLE, DELETED } -} \ No newline at end of file +} diff --git a/src/main/java/makamys/neodymium/util/Util.java b/src/main/java/makamys/neodymium/util/Util.java index cd43358..46cd0f0 100644 --- a/src/main/java/makamys/neodymium/util/Util.java +++ b/src/main/java/makamys/neodymium/util/Util.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; @@ -110,4 +111,9 @@ public class Util { public static int createBrightness(int sky, int block) { return sky << 20 | block << 4; } + + public static void setPositionAndLimit(Buffer buffer, int position, int limit) { + buffer.position(position); + buffer.limit(limit); + } } diff --git a/src/main/resources/shaders/chunk.vert b/src/main/resources/shaders/chunk.vert index 9988605..56bc2d6 100644 --- a/src/main/resources/shaders/chunk.vert +++ b/src/main/resources/shaders/chunk.vert @@ -19,7 +19,7 @@ uniform vec2 fogStartEnd; uniform int fogMode; uniform float fogDensity; -uniform vec3 playerPos; +uniform vec3 renderOffset; out vec2 TexCoord; #ifdef RPLE @@ -38,7 +38,8 @@ out float FogFactor; // -1 means: disable fog void main() { - gl_Position = proj * modelView * (vec4(aPos - playerPos, 1.0) + vec4(0, 0.12, 0, 0)); + vec4 untransformedPos = (vec4(aPos, 1.0) + vec4(renderOffset.x, renderOffset.y + 0.12, renderOffset.z, 0)); + gl_Position = proj * modelView * untransformedPos; TexCoord = aTexCoord; #ifdef RPLE BTexCoordR = aBTexCoordR; @@ -56,8 +57,7 @@ void main() if(fogStartEnd.x >= 0 && fogStartEnd.y >= 0){ float s = fogStartEnd.x; float e = fogStartEnd.y; - vec4 eyePos = (modelView * (vec4(aPos - playerPos, 1.0) + vec4(0, 0.12, 0, 0))); - float c = length(eyePos); + float c = length(untransformedPos); float fogFactor = fogMode == 0x2601 ? clamp((e - c) / (e - s), 0, 1) /* GL_LINEAR */ -- cgit