151 lines
5.5 KiB
C#
151 lines
5.5 KiB
C#
using OpenTK.Graphics.OpenGL4;
|
|
using OpenTK.Mathematics;
|
|
using OpenTK.Windowing.Common;
|
|
using OpenTK.Windowing.Desktop;
|
|
using OpenTK.Windowing.GraphicsLibraryFramework;
|
|
using Serilog;
|
|
using VoxelEngine.Game;
|
|
using VoxelEngine.Rendering;
|
|
using VoxelEngine.World.Blocks;
|
|
using VoxelEngine.World.Chunk;
|
|
using VoxelEngine.World.Persistence;
|
|
using VoxelEngine.World.Streaming;
|
|
using VoxelEngine.World.WorldGen;
|
|
|
|
namespace VoxelEngine.Core;
|
|
|
|
public sealed class Game : GameWindow
|
|
{
|
|
private static readonly ILogger Log = Serilog.Log.ForContext<Game>();
|
|
|
|
private readonly Configuration _cfg;
|
|
private readonly BlockRegistry _registry = new();
|
|
|
|
private Shader _shader = null!;
|
|
private Texture _texture = null!;
|
|
private LoadingScreen _loading = null!;
|
|
private World.World _world = null!;
|
|
private InMemoryChunkModificationStore _modStore = null!;
|
|
private WorldGenerator _generator = null!;
|
|
private ChunkStreamer _streamer = null!;
|
|
private Player _player = null!;
|
|
|
|
public Game(Configuration cfg, GameWindowSettings gameSettings, NativeWindowSettings nativeSettings)
|
|
: base(gameSettings, nativeSettings)
|
|
{
|
|
this._cfg = cfg;
|
|
}
|
|
|
|
protected override void OnLoad()
|
|
{
|
|
base.OnLoad();
|
|
|
|
Log.Information("Game starting with seed={Seed} renderDistanceH={H} renderDistanceV={V}",
|
|
this._cfg.Seed, this._cfg.RenderDistanceHorizontal, this._cfg.RenderDistanceVertical);
|
|
|
|
GL.ClearColor(0.45f, 0.65f, 0.95f, 1.0f);
|
|
GL.Enable(EnableCap.DepthTest);
|
|
GL.Enable(EnableCap.CullFace);
|
|
|
|
this._shader = new Shader("Assets/shaders/chunk.vert", "Assets/shaders/chunk.frag");
|
|
this._texture = new Texture("Assets/textures/VoxelShadeAtlas.png");
|
|
this._loading = new LoadingScreen();
|
|
|
|
this._modStore = new InMemoryChunkModificationStore();
|
|
this._world = new World.World(this._registry, this._modStore);
|
|
|
|
int seed = this._cfg.Seed ?? new Random().Next();
|
|
this._generator = new WorldGenerator(seed, this._registry, this._cfg);
|
|
|
|
// Spawn at terrain surface above (0, 0).
|
|
int surfaceY = this._generator.SurfaceHeightAt(0, 0);
|
|
Vector3 spawnPosition = new(0.5f, surfaceY + 2f, 0.5f);
|
|
|
|
this._player = new Player(this._cfg, spawnPosition, this.ClientSize.X / (float)this.ClientSize.Y);
|
|
|
|
var mesher = new ChunkMesher(this._texture.Width, this._texture.Height);
|
|
this._streamer = new ChunkStreamer(
|
|
this._cfg,
|
|
this._world,
|
|
this._generator.Generate,
|
|
mesher,
|
|
this._registry,
|
|
new ThreadPoolJobExecutor(),
|
|
mesh => new ChunkRenderer(mesh));
|
|
|
|
this.CursorState = CursorState.Grabbed;
|
|
}
|
|
|
|
protected override void OnUpdateFrame(FrameEventArgs args)
|
|
{
|
|
base.OnUpdateFrame(args);
|
|
if (!this.IsFocused) return;
|
|
|
|
if (this.KeyboardState.IsKeyDown(Keys.Escape)) { this.Close(); return; }
|
|
|
|
float speedMulti = this.KeyboardState.IsKeyDown(Keys.LeftShift) ? 1.8f : 1f;
|
|
float distance = this._player.MoveSpeed * speedMulti * (float)args.Time;
|
|
|
|
if (this.KeyboardState.IsKeyDown(Keys.W)) this._player.Move(this._player.Camera.Front * distance);
|
|
if (this.KeyboardState.IsKeyDown(Keys.S)) this._player.Move(-this._player.Camera.Front * distance);
|
|
if (this.KeyboardState.IsKeyDown(Keys.A)) this._player.Move(-this._player.Camera.Right * distance);
|
|
if (this.KeyboardState.IsKeyDown(Keys.D)) this._player.Move(this._player.Camera.Right * distance);
|
|
if (this.KeyboardState.IsKeyDown(Keys.Space)) this._player.Move(Vector3.UnitY * distance);
|
|
if (this.KeyboardState.IsKeyDown(Keys.LeftControl)) this._player.Move(-Vector3.UnitY * distance);
|
|
|
|
Vector2 delta = this.MouseState.Delta;
|
|
this._player.AddRotation(delta.X * this._player.MouseSensitivity, -delta.Y * this._player.MouseSensitivity);
|
|
|
|
this._streamer.Tick(this._player.ChunkPosition);
|
|
}
|
|
|
|
protected override void OnRenderFrame(FrameEventArgs args)
|
|
{
|
|
base.OnRenderFrame(args);
|
|
|
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
|
|
if (!this._streamer.SpawnReady)
|
|
{
|
|
this._loading.Draw();
|
|
this.SwapBuffers();
|
|
return;
|
|
}
|
|
|
|
this._shader.Use();
|
|
this._shader.SetMatrix4("view", this._player.Camera.GetViewMatrix());
|
|
this._shader.SetMatrix4("projection", this._player.Camera.GetProjectionMatrix());
|
|
this._shader.SetInt("atlas", 0);
|
|
this._shader.SetFloat("u_caveDarkStartY", this._cfg.CaveDarkStartY);
|
|
this._shader.SetFloat("u_caveDarkEndY", this._cfg.CaveDarkEndY);
|
|
this._shader.SetFloat("u_caveMinBrightness", this._cfg.CaveMinBrightness);
|
|
this._texture.Use(TextureUnit.Texture0);
|
|
|
|
this._streamer.DrawAll((pos, renderer) =>
|
|
{
|
|
this._shader.SetMatrix4("model", Matrix4.CreateTranslation(
|
|
pos.X * Chunk.Size, pos.Y * Chunk.Size, pos.Z * Chunk.Size));
|
|
renderer.Draw();
|
|
});
|
|
|
|
this.SwapBuffers();
|
|
}
|
|
|
|
protected override void OnResize(ResizeEventArgs e)
|
|
{
|
|
base.OnResize(e);
|
|
GL.Viewport(0, 0, e.Width, e.Height);
|
|
if (this._player is not null) this._player.Camera.AspectRatio = e.Width / (float)e.Height;
|
|
}
|
|
|
|
protected override void OnUnload()
|
|
{
|
|
Log.Information("Game shutting down");
|
|
this._streamer?.Dispose();
|
|
this._loading?.Dispose();
|
|
this._texture?.Dispose();
|
|
this._shader?.Dispose();
|
|
base.OnUnload();
|
|
}
|
|
}
|