149 lines
5.2 KiB
C#
149 lines
5.2 KiB
C#
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.Blocks;
|
|
using VoxelEngine.World.Chunk;
|
|
using VoxelEngine.World.Persistence;
|
|
using VoxelEngine.World.WorldGen;
|
|
|
|
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 const int Seed = 1337;
|
|
private const bool UseSeed = false;
|
|
|
|
private readonly BlockRegistry _registry = new();
|
|
private Shader _shader = null!;
|
|
private Texture _texture = null!;
|
|
private readonly Dictionary<Vector3i, ChunkRenderer> _renderer = new();
|
|
private Camera _camera = null!;
|
|
private WorldGenerator _generator = null!;
|
|
|
|
private World.World _world;
|
|
|
|
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);
|
|
|
|
this._shader = new Shader("Assets/shaders/chunk.vert", "Assets/shaders/chunk.frag");
|
|
this._texture = new Texture("Assets/textures/VoxelShadeAtlas.png");
|
|
|
|
this._world = new World.World(this._registry, new InMemoryChunkModificationStore());
|
|
|
|
int seed = Game.UseSeed ? Game.Seed : new Random().Next();
|
|
|
|
this._generator = new WorldGenerator(seed, this._registry);
|
|
|
|
for (int cx = -4; cx < 4; cx++)
|
|
for (int cz = -4; cz < 4; cz++)
|
|
for (int cy = 0; cy < WorldConstants.WorldHeightChunks; cy++)
|
|
{
|
|
Vector3i pos = new(cx, cy, cz);
|
|
this._world.SetChunk(pos, this._generator.Generate(pos));
|
|
}
|
|
|
|
ChunkMesher mesher = new(this._texture.Width, this._texture.Height);
|
|
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, WorldConstants.SeaLevel + 30f, 40f),
|
|
this.ClientSize.X / (float)this.ClientSize.Y);
|
|
|
|
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 = 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.LeftControl)) this._camera.Move(-Vector3.UnitY * distance);
|
|
|
|
Vector2 delta = this.MouseState.Delta;
|
|
this._camera.AddRotation(delta.X * Game.Sensitivity, -delta.Y * Game.Sensitivity);
|
|
}
|
|
|
|
protected override void OnRenderFrame(FrameEventArgs args)
|
|
{
|
|
base.OnRenderFrame(args);
|
|
|
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
|
|
this._shader.Use();
|
|
this._shader.SetMatrix4("view", this._camera.GetViewMatrix());
|
|
this._shader.SetMatrix4("projection", this._camera.GetProjectionMatrix());
|
|
this._shader.SetInt("atlas", 0);
|
|
this._texture.Use();
|
|
|
|
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();
|
|
}
|
|
|
|
protected override void OnResize(ResizeEventArgs e)
|
|
{
|
|
base.OnResize(e);
|
|
GL.Viewport(0, 0, e.Width, e.Height);
|
|
// OnResize can fire before OnLoad (where _camera is created), so guard against it.
|
|
if (this._camera is not null) this._camera.AspectRatio = e.Width / (float)e.Height;
|
|
}
|
|
|
|
protected override void OnUnload()
|
|
{
|
|
foreach (ChunkRenderer rendererValue in this._renderer.Values)
|
|
{
|
|
rendererValue.Dispose();
|
|
}
|
|
this._texture.Dispose();
|
|
this._shader.Dispose();
|
|
base.OnUnload();
|
|
}
|
|
}
|