feat: add multi-chunk world with cross-chunk face culling
Add a World that stores chunks in a Dictionary keyed by chunk coordinate, with floor-division world<->chunk coordinate mapping (correct for negative coords). The mesher takes an optional neighbor sampler so faces at chunk borders are culled against adjacent chunks instead of always meshing the shell. Render one ChunkRenderer per chunk, placed via the model matrix. Reorganize the World layer into Blocks/ and Chunk/ sub-namespaces.
This commit is contained in:
60
Core/Game.cs
60
Core/Game.cs
@@ -4,7 +4,8 @@ using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
using VoxelEngine.Rendering;
|
||||
using VoxelEngine.World;
|
||||
using VoxelEngine.World.Blocks;
|
||||
using VoxelEngine.World.Chunk;
|
||||
|
||||
namespace VoxelEngine.Core;
|
||||
|
||||
@@ -16,9 +17,11 @@ public sealed class Game : GameWindow
|
||||
private readonly BlockRegistry _registry = new();
|
||||
private Shader _shader = null!;
|
||||
private Texture _texture = null!;
|
||||
private ChunkRenderer _renderer = null!;
|
||||
private readonly Dictionary<Vector3i, ChunkRenderer> _renderer = new();
|
||||
private Camera _camera = null!;
|
||||
|
||||
private World.World _world;
|
||||
|
||||
public Game(GameWindowSettings gameSettings, NativeWindowSettings nativeSettings)
|
||||
: base(gameSettings, nativeSettings) { }
|
||||
|
||||
@@ -33,9 +36,28 @@ public sealed class Game : GameWindow
|
||||
this._shader = new Shader("Assets/shaders/chunk.vert", "Assets/shaders/chunk.frag");
|
||||
this._texture = new Texture("Assets/textures/atlas.png");
|
||||
|
||||
Chunk chunk = this.BuildChunk();
|
||||
float[] mesh = new ChunkMesher().Build(chunk, this._registry);
|
||||
this._renderer = new ChunkRenderer(mesh);
|
||||
this._world = new World.World(this._registry);
|
||||
|
||||
for (int x = -2; x < 2; x++)
|
||||
for (int z = -2; z < 2; z++)
|
||||
{
|
||||
this._world.SetChunk(new Vector3i(x, 0, z), this.BuildChunk());
|
||||
}
|
||||
|
||||
ChunkMesher mesher = new();
|
||||
foreach ((Vector3i pos, Chunk chunk) in this._world)
|
||||
{
|
||||
Vector3i origin = pos * Chunk.Size;
|
||||
float[] mesh = mesher.Build(
|
||||
chunk,
|
||||
this._registry,
|
||||
(nx, ny, nz) => this._world.GetBlock(
|
||||
origin.X + nx,
|
||||
origin.Y + ny,
|
||||
origin.Z + nz).IsOpaque
|
||||
);
|
||||
this._renderer[pos] = new ChunkRenderer(mesh);
|
||||
}
|
||||
|
||||
this._camera = new Camera(new Vector3(8f, 20f, 40f), this.ClientSize.X / (float)this.ClientSize.Y);
|
||||
|
||||
@@ -65,15 +87,20 @@ public sealed class Game : GameWindow
|
||||
if (!this.IsFocused) return;
|
||||
|
||||
if (this.KeyboardState.IsKeyDown(Keys.Escape)) {
|
||||
this.Close(); return; }
|
||||
this.Close(); return;
|
||||
}
|
||||
|
||||
float distance = Game.MoveSpeed * (float)args.Time;
|
||||
float speedMulti = 1f;
|
||||
|
||||
if (this.KeyboardState.IsKeyDown(Keys.LeftShift)) speedMulti = 1.8f ;
|
||||
|
||||
float distance = Game.MoveSpeed * speedMulti * (float)args.Time;
|
||||
if (this.KeyboardState.IsKeyDown(Keys.W)) this._camera.Move(this._camera.Front * distance);
|
||||
if (this.KeyboardState.IsKeyDown(Keys.S)) this._camera.Move(-this._camera.Front * distance);
|
||||
if (this.KeyboardState.IsKeyDown(Keys.A)) this._camera.Move(-this._camera.Right * distance);
|
||||
if (this.KeyboardState.IsKeyDown(Keys.D)) this._camera.Move(this._camera.Right * distance);
|
||||
if (this.KeyboardState.IsKeyDown(Keys.Space)) this._camera.Move(Vector3.UnitY * distance);
|
||||
if (this.KeyboardState.IsKeyDown(Keys.LeftShift)) this._camera.Move(-Vector3.UnitY * distance);
|
||||
if (this.KeyboardState.IsKeyDown(Keys.LeftControl)) this._camera.Move(-Vector3.UnitY * distance);
|
||||
|
||||
Vector2 delta = this.MouseState.Delta;
|
||||
this._camera.AddRotation(delta.X * Game.Sensitivity, -delta.Y * Game.Sensitivity);
|
||||
@@ -86,12 +113,20 @@ public sealed class Game : GameWindow
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
this._shader.Use();
|
||||
this._shader.SetMatrix4("model", Matrix4.Identity);
|
||||
this._shader.SetMatrix4("view", this._camera.GetViewMatrix());
|
||||
this._shader.SetMatrix4("projection", this._camera.GetProjectionMatrix());
|
||||
this._shader.SetInt("atlas", 0);
|
||||
this._texture.Use(TextureUnit.Texture0);
|
||||
this._renderer.Draw();
|
||||
|
||||
foreach ((Vector3i pos, ChunkRenderer renderer) in this._renderer)
|
||||
{
|
||||
this._shader.SetMatrix4("model", Matrix4.CreateTranslation(
|
||||
pos.X * Chunk.Size,
|
||||
pos.Y * Chunk.Size,
|
||||
pos.Z * Chunk.Size
|
||||
));
|
||||
renderer.Draw();
|
||||
}
|
||||
|
||||
this.SwapBuffers();
|
||||
}
|
||||
@@ -106,7 +141,10 @@ public sealed class Game : GameWindow
|
||||
|
||||
protected override void OnUnload()
|
||||
{
|
||||
this._renderer.Dispose();
|
||||
foreach (ChunkRenderer rendererValue in this._renderer.Values)
|
||||
{
|
||||
rendererValue.Dispose();
|
||||
}
|
||||
this._texture.Dispose();
|
||||
this._shader.Dispose();
|
||||
base.OnUnload();
|
||||
|
||||
Reference in New Issue
Block a user