using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; using OpenTK.Windowing.Common; using OpenTK.Windowing.Desktop; using OpenTK.Windowing.GraphicsLibraryFramework; using VoxelEngine.Rendering; using VoxelEngine.World; namespace VoxelEngine.Core; public sealed class Game : GameWindow { private const float MoveSpeed = 8f; // units per second private const float Sensitivity = 0.15f; // degrees per pixel private readonly BlockRegistry _registry = new(); private Shader _shader = null!; private Texture _texture = null!; private ChunkRenderer _renderer = null!; private Camera _camera = null!; public Game(GameWindowSettings gameSettings, NativeWindowSettings nativeSettings) : base(gameSettings, nativeSettings) { } protected override void OnLoad() { base.OnLoad(); GL.ClearColor(0.45f, 0.65f, 0.95f, 1.0f); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.CullFace); _shader = new Shader("Assets/shaders/chunk.vert", "Assets/shaders/chunk.frag"); _texture = new Texture("Assets/textures/atlas.png"); Chunk chunk = BuildChunk(); float[] mesh = new ChunkMesher().Build(chunk, _registry); _renderer = new ChunkRenderer(mesh); _camera = new Camera(new Vector3(8f, 20f, 40f), ClientSize.X / (float)ClientSize.Y); CursorState = CursorState.Grabbed; } private Chunk BuildChunk() { var chunk = new Chunk(); for (int x = 0; x < Chunk.Size; x++) for (int z = 0; z < Chunk.Size; z++) for (int y = 0; y < Chunk.Size; y++) { if (y == Chunk.Size - 1) chunk[x, y, z] = _registry.Grass.Id; else if (y >= Chunk.Size - 4) chunk[x, y, z] = _registry.Dirt.Id; else chunk[x, y, z] = _registry.Stone.Id; } return chunk; } protected override void OnUpdateFrame(FrameEventArgs args) { base.OnUpdateFrame(args); if (!IsFocused) return; KeyboardState keyboard = KeyboardState; if (keyboard.IsKeyDown(Keys.Escape)) { Close(); return; } float distance = MoveSpeed * (float)args.Time; if (keyboard.IsKeyDown(Keys.W)) _camera.Move(_camera.Front * distance); if (keyboard.IsKeyDown(Keys.S)) _camera.Move(-_camera.Front * distance); if (keyboard.IsKeyDown(Keys.A)) _camera.Move(-_camera.Right * distance); if (keyboard.IsKeyDown(Keys.D)) _camera.Move(_camera.Right * distance); if (keyboard.IsKeyDown(Keys.Space)) _camera.Move(Vector3.UnitY * distance); if (keyboard.IsKeyDown(Keys.LeftShift)) _camera.Move(-Vector3.UnitY * distance); Vector2 delta = MouseState.Delta; _camera.AddRotation(delta.X * Sensitivity, -delta.Y * Sensitivity); } protected override void OnRenderFrame(FrameEventArgs args) { base.OnRenderFrame(args); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); _shader.Use(); _shader.SetMatrix4("model", Matrix4.Identity); _shader.SetMatrix4("view", _camera.GetViewMatrix()); _shader.SetMatrix4("projection", _camera.GetProjectionMatrix()); _shader.SetInt("atlas", 0); _texture.Use(TextureUnit.Texture0); _renderer.Draw(); SwapBuffers(); } protected override void OnResize(ResizeEventArgs e) { base.OnResize(e); GL.Viewport(0, 0, e.Width, e.Height); if (_camera is not null) _camera.AspectRatio = e.Width / (float)e.Height; } protected override void OnUnload() { _renderer.Dispose(); _texture.Dispose(); _shader.Dispose(); base.OnUnload(); } }