feat(streaming): load full column bedrock->surface to fix terrain holes
This commit is contained in:
@@ -10,7 +10,6 @@ public class ConfigurationTests
|
||||
{
|
||||
var c = new Configuration();
|
||||
Assert.True(c.RenderDistanceHorizontal >= 1);
|
||||
Assert.True(c.RenderDistanceVertical >= 1);
|
||||
Assert.True(c.SpawnReadyRadiusHorizontal >= 0);
|
||||
Assert.True(c.MaxGenerateJobsInFlight >= 1);
|
||||
Assert.True(c.MaxMeshUploadsPerFrame >= 1);
|
||||
@@ -32,7 +31,6 @@ public class ConfigurationTests
|
||||
|
||||
var c2 = new Configuration
|
||||
{
|
||||
RenderDistanceVertical = 2,
|
||||
SpawnReadyRadiusHorizontal = 0,
|
||||
MaxGenerateJobsInFlight = 2,
|
||||
MaxMeshUploadsPerFrame = 1,
|
||||
@@ -43,7 +41,6 @@ public class ConfigurationTests
|
||||
WindowWidth = 800,
|
||||
WindowHeight = 600,
|
||||
};
|
||||
Assert.Equal(2, c2.RenderDistanceVertical);
|
||||
Assert.Equal(0, c2.SpawnReadyRadiusHorizontal);
|
||||
Assert.Equal(2, c2.MaxGenerateJobsInFlight);
|
||||
Assert.Equal(1, c2.MaxMeshUploadsPerFrame);
|
||||
@@ -80,7 +77,6 @@ public class ConfigurationTests
|
||||
{
|
||||
var c = new Configuration();
|
||||
|
||||
Assert.Equal(1, c.SurfaceShellSkirtBelow);
|
||||
Assert.Equal(0.7f, c.FogStartFraction);
|
||||
Assert.Equal(new OpenTK.Mathematics.Vector3(0.45f, 0.65f, 0.95f), c.SkyColor);
|
||||
Assert.Equal(256, c.MinCaveY);
|
||||
|
||||
@@ -6,6 +6,7 @@ using VoxelEngine.World.Blocks;
|
||||
using VoxelEngine.World.Chunk;
|
||||
using VoxelEngine.World.Persistence;
|
||||
using VoxelEngine.World.Streaming;
|
||||
using VoxelEngine.World.WorldGen;
|
||||
using Xunit;
|
||||
using VoxelChunk = VoxelEngine.World.Chunk.Chunk;
|
||||
|
||||
@@ -72,7 +73,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -97,7 +97,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -130,7 +129,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 2, // cap drains per tick
|
||||
};
|
||||
@@ -151,19 +149,21 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 2,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
var (streamer, world, created) = CreateStreamer(cfg);
|
||||
// Surface far above the world ceiling -> top chunk clamps to
|
||||
// WorldHeightChunks-1 (31). Full column loads cy 0..31 and nothing above.
|
||||
var (streamer, world, _) = CreateStreamer(cfg, surfaceHeightAt: (_, _) => 100_000);
|
||||
|
||||
// Player at y=0 (chunk Y = 0). V=2 wants Y ∈ [-2, 2], but valid Y is [0, 8).
|
||||
// So only Y ∈ {0, 1, 2} qualifies → 1 × 3 × 1 = 3 chunks.
|
||||
streamer.Tick(new Vector3i(0, 0, 0));
|
||||
Assert.Equal(3, created.Count);
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 0, 0)));
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 1, 0)));
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 2, 0)));
|
||||
|
||||
for (int cy = 0; cy < WorldConstants.WorldHeightChunks; cy++)
|
||||
{
|
||||
var pos = new Vector3i(0, cy, 0);
|
||||
Assert.NotNull(world.GetChunk(pos));
|
||||
Assert.True(streamer.IsReady(pos));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -172,7 +172,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -201,7 +200,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -224,7 +222,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -251,7 +248,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
SpawnReadyRadiusHorizontal = 1,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 1, // throttle to expose progression
|
||||
@@ -271,7 +267,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -296,7 +291,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -318,7 +312,7 @@ public class ChunkStreamerTests
|
||||
[Fact]
|
||||
public void MarkDirty_OnUntrackedChunk_IsNoOp()
|
||||
{
|
||||
var cfg = new Configuration { RenderDistanceHorizontal = 0, RenderDistanceVertical = 0 };
|
||||
var cfg = new Configuration { RenderDistanceHorizontal = 0 };
|
||||
var (streamer, _, created) = CreateStreamer(cfg);
|
||||
|
||||
streamer.MarkDirty(new Vector3i(99, 0, 99));
|
||||
@@ -334,7 +328,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -368,7 +361,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -400,20 +392,20 @@ public class ChunkStreamerTests
|
||||
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);
|
||||
// Surface at worldY 48 -> top chunk 3, so a full column is cy 0..3 and
|
||||
// the player at Y=2 has in-column vertical neighbours at Y=1 and Y=3.
|
||||
var (streamer, world, _) = CreateStreamer(cfg, surfaceHeightAt: (_, _) => 48);
|
||||
|
||||
// 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.
|
||||
// (Y=1, Y=3) have priority 4 -- they must 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)));
|
||||
@@ -424,27 +416,55 @@ public class ChunkStreamerTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tick_LoadsSurfaceShell_AndExcludesAirAboveSurface()
|
||||
public void Tick_LoadsFullColumn_FromBedrockToSurface()
|
||||
{
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0, // single column at the player
|
||||
RenderDistanceVertical = 0, // slab is just the player's chunk
|
||||
SurfaceShellSkirtBelow = 1,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
// Surface at worldY 40 → chunk Y 2. Shell = [2-1, 2] = {1, 2}. Slab at
|
||||
// player Y=0 = {0}. Union = {0, 1, 2}. Chunk Y 3+ is air above surface.
|
||||
// Surface at worldY 40 -> top chunk floor(40/16) = 2. Full column loads
|
||||
// cy 0,1,2; chunk Y 3+ is air above the surface and must NOT load.
|
||||
var (streamer, world, _) = CreateStreamer(cfg, surfaceHeightAt: (_, _) => 40);
|
||||
|
||||
streamer.Tick(new Vector3i(0, 0, 0));
|
||||
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 0, 0))); // slab
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 1, 0))); // shell skirt
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 2, 0))); // shell surface
|
||||
Assert.Null(world.GetChunk(new Vector3i(0, 3, 0))); // air above surface — not loaded
|
||||
Assert.Null(world.GetChunk(new Vector3i(0, 5, 0))); // air above surface — not loaded
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 0, 0))); // bedrock
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 1, 0))); // mid
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 2, 0))); // surface
|
||||
Assert.Null(world.GetChunk(new Vector3i(0, 3, 0))); // air above surface
|
||||
Assert.Null(world.GetChunk(new Vector3i(0, 5, 0))); // air above surface
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tick_LoadsEditedSectionAboveSurface()
|
||||
{
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
var registry = new BlockRegistry();
|
||||
var store = new InMemoryChunkModificationStore();
|
||||
var world = new World.World(registry, store);
|
||||
var mesher = new FakeMesher();
|
||||
var executor = new SynchronousJobExecutor();
|
||||
Func<float[], IChunkRenderer> factory = m => new FakeRenderer(m);
|
||||
Func<Vector3i, VoxelChunk> generate = _ => new VoxelChunk();
|
||||
|
||||
// Surface at worldY 8 -> top chunk 0. A player edit sits in chunk Y 5,
|
||||
// far above the surface; that edited section must still load.
|
||||
var edited = new Vector3i(0, 5, 0);
|
||||
store.RecordEdit(edited, 0, registry.Stone.Id);
|
||||
|
||||
var streamer = new ChunkStreamer(cfg, world, generate, (_, _) => 8, mesher, registry, executor, factory);
|
||||
streamer.Tick(new Vector3i(0, 0, 0));
|
||||
|
||||
Assert.NotNull(world.GetChunk(new Vector3i(0, 0, 0))); // surface column
|
||||
Assert.NotNull(world.GetChunk(edited)); // edited air section above surface
|
||||
Assert.Null(world.GetChunk(new Vector3i(0, 3, 0))); // unedited air -- not loaded
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -453,7 +473,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
SpawnReadyRadiusHorizontal = 1,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
@@ -476,7 +495,6 @@ public class ChunkStreamerTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
|
||||
@@ -32,7 +32,6 @@ public class StreamingIntegrationTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 1,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
@@ -58,7 +57,6 @@ public class StreamingIntegrationTests
|
||||
var cfg = new Configuration
|
||||
{
|
||||
RenderDistanceHorizontal = 0,
|
||||
RenderDistanceVertical = 0,
|
||||
MaxGenerateJobsInFlight = 100,
|
||||
MaxMeshUploadsPerFrame = 100,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user