diff --git a/Core/Configuration.cs b/Core/Configuration.cs index c423ad7..c41adad 100644 --- a/Core/Configuration.cs +++ b/Core/Configuration.cs @@ -10,6 +10,11 @@ public sealed class Configuration public int MaxMeshUploadsPerFrame { get; set; } = 4; public int WorkerThreadCount { get; set; } = Math.Max(1, Environment.ProcessorCount / 2); + // Multiplier applied to (dy)^2 in chunk-load priority. >1 prioritizes + // chunks at the player's Y over equally-distant chunks above/below, so the + // visible surface fills in before far-underground / overhead chunks. + public int VerticalPriorityWeight { get; set; } = 4; + public float MoveSpeed { get; set; } = 8f; public float MouseSensitivity { get; set; } = 0.15f; public float Fov { get; set; } = 60f; diff --git a/VoxelEngine.Tests/Streaming/ChunkStreamerTests.cs b/VoxelEngine.Tests/Streaming/ChunkStreamerTests.cs index 5f42087..00211b7 100644 --- a/VoxelEngine.Tests/Streaming/ChunkStreamerTests.cs +++ b/VoxelEngine.Tests/Streaming/ChunkStreamerTests.cs @@ -351,4 +351,33 @@ public class ChunkStreamerTests streamer.Tick(new Vector3i(0, 0, 0)); Assert.Equal(1, generateCalls); } + + [Fact] + public void Tick_VerticalPriorityWeight_PrefersHorizontalAtSameDistance() + { + var cfg = new Configuration + { + RenderDistanceHorizontal = 1, + RenderDistanceVertical = 1, + MaxGenerateJobsInFlight = 100, + MaxMeshUploadsPerFrame = 5, // player's chunk + 4 horizontal cardinals + VerticalPriorityWeight = 4, + }; + var (streamer, world, _) = CreateStreamer(cfg); + + // Player chunk at Y=2 so vertical neighbors Y=1 and Y=3 are valid. + var playerPos = new Vector3i(0, 2, 0); + streamer.Tick(playerPos); + + // Drain budget = 5 = priority 0 (player) + 4 priority-1 (horizontal + // cardinals). With VerticalPriorityWeight=4, vertical cardinals + // (Y=1, Y=3) have priority 4 — they should NOT be among the first 5. + Assert.NotNull(world.GetChunk(playerPos)); + Assert.NotNull(world.GetChunk(new Vector3i( 1, 2, 0))); + Assert.NotNull(world.GetChunk(new Vector3i(-1, 2, 0))); + Assert.NotNull(world.GetChunk(new Vector3i( 0, 2, 1))); + Assert.NotNull(world.GetChunk(new Vector3i( 0, 2, -1))); + Assert.Null(world.GetChunk(new Vector3i(0, 3, 0))); + Assert.Null(world.GetChunk(new Vector3i(0, 1, 0))); + } } diff --git a/World/Streaming/ChunkStreamer.cs b/World/Streaming/ChunkStreamer.cs index 8a4b3ea..c031d6d 100644 --- a/World/Streaming/ChunkStreamer.cs +++ b/World/Streaming/ChunkStreamer.cs @@ -122,9 +122,12 @@ public sealed class ChunkStreamer : IDisposable if (this._failed.Contains(pos)) continue; if (this._tracked.Add(pos)) { - int priority = (pos.X - p.X) * (pos.X - p.X) - + (pos.Y - p.Y) * (pos.Y - p.Y) - + (pos.Z - p.Z) * (pos.Z - p.Z); + int dx = pos.X - p.X; + int dy = pos.Y - p.Y; + int dz = pos.Z - p.Z; + int priority = dx * dx + + dz * dz + + dy * dy * this._cfg.VerticalPriorityWeight; this._pendingLoad.Enqueue(pos, priority); } }