diff --git a/Player.cs b/Player.cs index 753c1b4..f71bbfc 100644 --- a/Player.cs +++ b/Player.cs @@ -48,6 +48,8 @@ namespace Voxel public new void Tick() { + _world.UpdateChunkLoading(Position); + previousPosition = Position; float forwards = 0; diff --git a/Program.cs b/Program.cs index 31ea42d..0cf28ff 100644 --- a/Program.cs +++ b/Program.cs @@ -14,8 +14,8 @@ internal class Program Console.WriteLine("Generating map..."); - int worldSizeX = 8; - int worldSizeY = 8; + int worldSizeX = 2; + int worldSizeY = 2; float maxI = worldSizeX * worldSizeY; int i = 0; diff --git a/Renderer.cs b/Renderer.cs index 64fb6c6..d40c9df 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -61,11 +61,12 @@ namespace Voxel if (_world == null) return; int offset = 0; + foreach (Chunk chunk in _world.GetAllChunks()) { ChunkMesh chunkMesh = chunk.GetChunkMesh(); - if (chunkMesh.NeedsUpdate) + if (chunkMesh.NeedsUpdate) // always true { byte[] data = chunkMesh.GetPackedData(); GL.BufferSubData(BufferTarget.ShaderStorageBuffer, (IntPtr)offset * 8, chunkMesh.Size * 8, data); diff --git a/Shaders/shader.frag b/Shaders/shader.frag index 7a31ca3..c985cc5 100644 --- a/Shaders/shader.frag +++ b/Shaders/shader.frag @@ -10,16 +10,23 @@ uniform vec3 cameraPosition; void main() { - float fogEnd = 512; - float fogStart = 32; - - float dist = length(cameraPosition - fragPos); - float fogFactor = (fogEnd - dist) / (fogEnd - fogStart); - fogFactor = clamp(fogFactor, 0, 1); - - vec4 fogColor = vec4(0.8,0.8,0.8,1); - vec4 texColor = texture(uTexture, fragUV) * lighting; - vec4 color = mix(fogColor, texColor, fogFactor); - - FragColor = vec4(color.rgb, texColor.a); + // Use squared distance + vec3 delta = cameraPosition - fragPos; + float distSq = dot(delta, delta); + + // Precomputed fog parameters + const float fogEndSq = 16384.0; // 128^2 + const float fogStartSq = 1024.0; // 32^2 + const float fogRangeInv = 1.0 / (fogEndSq - fogStartSq); + + float fogFactor = (fogEndSq - distSq) * fogRangeInv; + fogFactor = clamp(fogFactor, 0.0, 1.0); + + // Texture and lighting + vec4 texColor = texture(uTexture, fragUV); + texColor.rgb *= lighting; + + // Fog blend + const vec3 fogColor = vec3(0.8, 0.8, 0.8); + FragColor = vec4(mix(fogColor, texColor.rgb, fogFactor), texColor.a); } \ No newline at end of file diff --git a/Shaders/shader.vert b/Shaders/shader.vert index 2b35d53..8d1aa25 100644 --- a/Shaders/shader.vert +++ b/Shaders/shader.vert @@ -82,7 +82,7 @@ void main() uint faceIndex = gl_VertexID / 6u; uint vertIndex = gl_VertexID % 6u; - uint start = faceIndex * 2u; // 2 byte per face + uint start = faceIndex * 2u; // 2 uint per face uint u0 = faces[start]; // data in uint 0 uint u1 = faces[start + 1]; // data in uint 1 @@ -95,7 +95,8 @@ void main() uint texture = u1 & 0xFFu; uint lightLevel = (u1 >> 8) & 0xFFu; - vec3 basePos = vec3(x + chunkX * 16, y, z + chunkY * 16); + vec3 basePos = vec3(x, y, z) + vec3(chunkX, 0, chunkY) * 16.0; + vec4 worldPos = vec4(basePos + offsets[facing][vertIndex], 1.0); float light = float(lightLevel) / 255.0; // use later for caves and stuff diff --git a/Window.cs b/Window.cs index 0df6f2c..d6440fe 100644 --- a/Window.cs +++ b/Window.cs @@ -90,7 +90,7 @@ namespace Voxel GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); CursorState = CursorState.Grabbed; - //VSync = VSyncMode.On; + VSync = VSyncMode.On; Camera.UpdateSize(Width, Height); } diff --git a/World.cs b/World.cs index a8a83bf..7db3a9a 100644 --- a/World.cs +++ b/World.cs @@ -8,6 +8,9 @@ namespace Voxel { private Dictionary<(int, int), Chunk> _chunks; + private (int x, int z) _lastCenter = (0, 0); + private int _loadDistance = 4; + private static readonly Dictionary _neighborOffsets = new() { { Orientation.West, (1, 0) }, @@ -33,6 +36,7 @@ namespace Voxel UpdateNeighboringChunks(chunk); chunk.UpdateChunkMesh(); + Renderer.MarkBuffersDirty(); } public void RemoveChunk(int chunkX, int chunkZ) @@ -52,6 +56,63 @@ namespace Voxel } } + public new void UpdateChunkLoading(Vector3 playerPosition) + { + int centerX = (int)Math.Floor(playerPosition.X / Chunk.Size); + int centerZ = (int)Math.Floor(playerPosition.Z / Chunk.Size); + + // Quick check - skip if still in same chunk + if (centerX == _lastCenter.x && centerZ == _lastCenter.z) + return; + + _lastCenter = (centerX, centerZ); + + // Calculate bounds + int minX = centerX - _loadDistance; + int maxX = centerX + _loadDistance; + int minZ = centerZ - _loadDistance; + int maxZ = centerZ + _loadDistance; + + // Unload chunks outside range + UnloadDistantChunks(minX, maxX, minZ, maxZ); + + // Load chunks inside range + LoadChunksInRange(minX, maxX, minZ, maxZ); + } + + private void UnloadDistantChunks(int minX, int maxX, int minZ, int maxZ) + { + var chunksToRemove = new List<(int, int)>(); + + foreach (var (chunkX, chunkZ) in _chunks.Keys) + { + if (chunkX < minX || chunkX > maxX || chunkZ < minZ || chunkZ > maxZ) + { + chunksToRemove.Add((chunkX, chunkZ)); + } + } + + foreach (var chunkPos in chunksToRemove) + { + RemoveChunk(chunkPos.Item1, chunkPos.Item2); + } + } + + private void LoadChunksInRange(int minX, int maxX, int minZ, int maxZ) + { + for (int x = minX; x <= maxX; x++) + { + for (int z = minZ; z <= maxZ; z++) + { + if (!_chunks.ContainsKey((x, z))) + { + Chunk chunk = new Chunk(x, z); + AddChunk(chunk); + } + } + } + } + public void UpdateNeighboringChunks(Chunk chunk) { chunk.Neighbors[Orientation.West] = null; @@ -87,39 +148,21 @@ namespace Voxel public Blocks GetBlock(int worldX, int worldY, int worldZ) { - int chunkX = worldX / Chunk.Size; - int chunkZ = worldZ / Chunk.Size; + var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ); + Chunk chunk = GetChunk(chunkX, chunkZ); if (chunk == null) return 0; // air if chunk not loaded - int localX = worldX % Chunk.Size; - int localZ = worldZ % Chunk.Size; - return chunk.GetBlock(localX, worldY, localZ); } - List GetEdgeOrientations(int localX, int localZ, int size) - { - var orientations = new List(); - - if (localX == size - 1) orientations.Add(Orientation.West); - if (localX == 0) orientations.Add(Orientation.East); - if (localZ == size - 1) orientations.Add(Orientation.North); - if (localZ == 0) orientations.Add(Orientation.South); - - return orientations; - } - public void SetBlock(int worldX, int worldY, int worldZ, Blocks block) { - int chunkX = worldX / Chunk.Size; - int chunkZ = worldZ / Chunk.Size; + var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ); + Chunk chunk = GetChunk(chunkX, chunkZ); if (chunk == null) return; - int localX = worldX % Chunk.Size; - int localZ = worldZ % Chunk.Size; - chunk.SetBlock(localX, worldY, localZ, block); if (block == Blocks.Air && ((localX == Chunk.Size - 1 || localX == 0) || (localZ == Chunk.Size - 1 || localZ == 0))) @@ -134,6 +177,30 @@ namespace Voxel } } + public static (int chunkX, int chunkZ, int localX, int localZ) WorldToChunkCoords(int worldX, int worldZ) + { + int chunkX = worldX >= 0 ? worldX / Chunk.Size : (worldX + 1) / Chunk.Size - 1; + int chunkZ = worldZ >= 0 ? worldZ / Chunk.Size : (worldZ + 1) / Chunk.Size - 1; + + int localX = ((worldX % Chunk.Size) + Chunk.Size) % Chunk.Size; + int localZ = ((worldZ % Chunk.Size) + Chunk.Size) % Chunk.Size; + + return (chunkX, chunkZ, localX, localZ); + } + + + List GetEdgeOrientations(int localX, int localZ, int size) + { + var orientations = new List(); + + if (localX == size - 1) orientations.Add(Orientation.West); + if (localX == 0) orientations.Add(Orientation.East); + if (localZ == size - 1) orientations.Add(Orientation.North); + if (localZ == 0) orientations.Add(Orientation.South); + + return orientations; + } + public IEnumerable<(Orientation orientation, Chunk neighbor)> GetChunkNeighbors(Chunk chunk) { int chunkX = chunk.X;