Compare commits
10 Commits
1d062528aa
...
50f9d4c0c8
| Author | SHA1 | Date | |
|---|---|---|---|
| 50f9d4c0c8 | |||
|
|
ce456b6b26 | ||
|
|
b6655d71d9 | ||
|
|
3678eaa5f8 | ||
| 94ebc4ace4 | |||
| 00713db79e | |||
| a9cab195b6 | |||
|
|
6fb19c415f | ||
|
|
71c5f3a3aa | ||
| b6f9966eb9 |
7
BlockData.cs
Normal file
7
BlockData.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public class BlockData
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
89
Blocks.cs
Normal file
89
Blocks.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public enum Blocks : byte
|
||||||
|
{
|
||||||
|
Air,
|
||||||
|
Stone,
|
||||||
|
Dirt,
|
||||||
|
OakPlanks,
|
||||||
|
Grass,
|
||||||
|
Bedrock
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Orientation : byte
|
||||||
|
{
|
||||||
|
West = 0, // + X
|
||||||
|
East = 1, // - X
|
||||||
|
Top = 2, // + Y
|
||||||
|
Bottom = 3,// - Y
|
||||||
|
North = 4, // + Z
|
||||||
|
South = 5, // - Z
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockDefinition
|
||||||
|
{
|
||||||
|
public Blocks BlockType;
|
||||||
|
public Textures[] FaceTextures;
|
||||||
|
|
||||||
|
public BlockDefinition(Blocks type, Textures singleTexture)
|
||||||
|
{
|
||||||
|
BlockType = type;
|
||||||
|
FaceTextures = new Voxel.Textures[6];
|
||||||
|
for (int i = 0; i < 6; i++) FaceTextures[i] = singleTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockDefinition(
|
||||||
|
Blocks type,
|
||||||
|
Textures west,
|
||||||
|
Textures east,
|
||||||
|
Textures top,
|
||||||
|
Textures bottom,
|
||||||
|
Textures north,
|
||||||
|
Textures south
|
||||||
|
)
|
||||||
|
{
|
||||||
|
BlockType = type;
|
||||||
|
FaceTextures = new Textures[]
|
||||||
|
{
|
||||||
|
west, east, top, bottom, north, south
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BlockDefinitions
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<Blocks, BlockDefinition> Blocks;
|
||||||
|
|
||||||
|
static BlockDefinitions()
|
||||||
|
{
|
||||||
|
Blocks = new Dictionary<Blocks, BlockDefinition>
|
||||||
|
{
|
||||||
|
{Voxel.Blocks.Stone, new BlockDefinition(
|
||||||
|
Voxel.Blocks.Stone, Textures.Stone
|
||||||
|
)},
|
||||||
|
|
||||||
|
{Voxel.Blocks.Dirt, new BlockDefinition(
|
||||||
|
Voxel.Blocks.Dirt, Textures.Dirt
|
||||||
|
)},
|
||||||
|
|
||||||
|
{Voxel.Blocks.OakPlanks, new BlockDefinition(
|
||||||
|
Voxel.Blocks.OakPlanks, Textures.OakPlanks
|
||||||
|
)},
|
||||||
|
|
||||||
|
{Voxel.Blocks.Bedrock, new BlockDefinition(
|
||||||
|
Voxel.Blocks.Bedrock, Textures.Bedrock
|
||||||
|
)},
|
||||||
|
|
||||||
|
{ Voxel.Blocks.Grass, new BlockDefinition(
|
||||||
|
Voxel.Blocks.Grass,
|
||||||
|
Voxel.Textures.GrassSide, // West
|
||||||
|
Voxel.Textures.GrassSide, // East
|
||||||
|
Voxel.Textures.GrassTop, // Top
|
||||||
|
Voxel.Textures.Dirt, // Bottom
|
||||||
|
Voxel.Textures.GrassSide, // North
|
||||||
|
Voxel.Textures.GrassSide // South
|
||||||
|
)},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Camera.cs
Normal file
76
Camera.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
static class Camera
|
||||||
|
{
|
||||||
|
public static Vector3 Position = new Vector3(-8, 16, -8);
|
||||||
|
|
||||||
|
public static float Pitch = -22.5f;
|
||||||
|
public static float Yaw = 45f;
|
||||||
|
public static float FOV = 60f;
|
||||||
|
public static float Speed = 5f;
|
||||||
|
public static float ShiftSpeed = 20f;
|
||||||
|
|
||||||
|
public static Matrix4 view =>
|
||||||
|
Matrix4.LookAt(Position, Position + Front, Vector3.UnitY);
|
||||||
|
|
||||||
|
public static Matrix4 projection;
|
||||||
|
|
||||||
|
public static Vector3 Front
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Vector3 front;
|
||||||
|
front.X = MathF.Cos(MathHelper.DegreesToRadians(Yaw)) * MathF.Cos(MathHelper.DegreesToRadians(Pitch));
|
||||||
|
front.Y = MathF.Sin(MathHelper.DegreesToRadians(Pitch));
|
||||||
|
front.Z = MathF.Sin(MathHelper.DegreesToRadians(Yaw)) * MathF.Cos(MathHelper.DegreesToRadians(Pitch));
|
||||||
|
return front.Normalized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Update(float time)
|
||||||
|
{
|
||||||
|
float moveSpeed = Speed * time;
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.LeftShift))
|
||||||
|
{
|
||||||
|
moveSpeed = ShiftSpeed * time;
|
||||||
|
}
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.W))
|
||||||
|
{
|
||||||
|
Position += Front * moveSpeed;
|
||||||
|
}
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.S))
|
||||||
|
{
|
||||||
|
Position += Front * -moveSpeed;
|
||||||
|
}
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.A))
|
||||||
|
{
|
||||||
|
Position += Vector3.Cross(Front, Vector3.UnitY).Normalized() * -moveSpeed;
|
||||||
|
}
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.D))
|
||||||
|
{
|
||||||
|
Position += Vector3.Cross(Front, Vector3.UnitY).Normalized() * moveSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateMouse(Vector2 delta)
|
||||||
|
{
|
||||||
|
float sensitivity = 0.1f;
|
||||||
|
Yaw += delta.X * sensitivity;
|
||||||
|
Pitch -= delta.Y * sensitivity;
|
||||||
|
|
||||||
|
Pitch = MathHelper.Clamp(Pitch, -89f, 89f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateProjection(int width, int height)
|
||||||
|
{
|
||||||
|
float fov = MathHelper.DegreesToRadians(FOV);
|
||||||
|
float aspectRatio = width / (float)height;
|
||||||
|
float near = 0.1f;
|
||||||
|
float far = 1000f;
|
||||||
|
|
||||||
|
projection = Matrix4.CreatePerspectiveFieldOfView(fov, aspectRatio, near, far);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
Chunk.cs
Normal file
153
Chunk.cs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public class Chunk
|
||||||
|
{
|
||||||
|
public static int Size = 16;
|
||||||
|
public static int Height = 256;
|
||||||
|
public readonly int X;
|
||||||
|
public readonly int Y;
|
||||||
|
private ChunkMesh _chunkMesh;
|
||||||
|
|
||||||
|
private Dictionary<ushort, BlockData> _blockData;
|
||||||
|
private Blocks[] _blocks;
|
||||||
|
|
||||||
|
public Chunk(int x, int y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
|
||||||
|
_blockData = new Dictionary<ushort, BlockData>();
|
||||||
|
_blocks = new Blocks[Size * Size * Height];
|
||||||
|
_chunkMesh = new ChunkMesh(X, Y);
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBlock(int x, int y, int z, Blocks block)
|
||||||
|
{
|
||||||
|
int i = GetBlockIndex(x, y, z);
|
||||||
|
if (i == -1) return;
|
||||||
|
_blocks[i] = block;
|
||||||
|
UpdateChunkMesh();
|
||||||
|
|
||||||
|
Renderer.MarkBuffersDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBlockIndex(int i, Blocks block)
|
||||||
|
{
|
||||||
|
_blocks[i] = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Blocks GetBlock(int x, int y, int z)
|
||||||
|
{
|
||||||
|
int i = GetBlockIndex(x, y, z);
|
||||||
|
if (i == -1) return Blocks.Air;
|
||||||
|
return _blocks[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockData GetBlockData(int x, int y, int z)
|
||||||
|
{
|
||||||
|
return new BlockData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Size * Size; i++)
|
||||||
|
_blocks[i] = Blocks.Bedrock;
|
||||||
|
for (int i = Size * Size * 1; i < Size * Size * 15; i++)
|
||||||
|
_blocks[i] = Blocks.Stone;
|
||||||
|
for (int i = Size * Size * 15; i < Size * Size * 16; i++)
|
||||||
|
_blocks[i] = Blocks.Grass;
|
||||||
|
UpdateChunkMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo
|
||||||
|
public (int x, int y, int z) IndexToPosition(int i)
|
||||||
|
{
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
int z = 0;
|
||||||
|
|
||||||
|
return (x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetBlockIndex(int x, int y, int z)
|
||||||
|
{
|
||||||
|
if (x < 0 || x > 15 || y < 0 || y > 255 || z < 0 || z > 15)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return x + z * Size + y * Size * Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly (int dx, int dy, int dz)[] Offsets = new (int, int, int)[6]
|
||||||
|
{
|
||||||
|
( 1, 0, 0), // +X
|
||||||
|
(-1, 0, 0), // -X
|
||||||
|
( 0, 1, 0), // +Y
|
||||||
|
( 0, -1, 0), // -Y
|
||||||
|
( 0, 0, 1), // +Z
|
||||||
|
( 0, 0, -1) // -Z
|
||||||
|
};
|
||||||
|
|
||||||
|
private void UpdateChunkMesh()
|
||||||
|
{
|
||||||
|
List<FaceData> faces = new List<FaceData>(Size * Size * Height / 2);
|
||||||
|
|
||||||
|
for (int x = 0; x < Size; x++)
|
||||||
|
{
|
||||||
|
for (int z = 0; z < Size; z++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < Height; y++)
|
||||||
|
{
|
||||||
|
for (byte face = 0; face < 6; face++)
|
||||||
|
{
|
||||||
|
int indexBase = y * Size * Size + z * Size + x;
|
||||||
|
Blocks block = _blocks[indexBase];
|
||||||
|
|
||||||
|
void AddFace()
|
||||||
|
{
|
||||||
|
FaceData faceData = new FaceData();
|
||||||
|
|
||||||
|
faceData.Facing = (Orientation)face;
|
||||||
|
faceData.Texture = BlockDefinitions.Blocks[block].FaceTextures[face];
|
||||||
|
|
||||||
|
faceData.X = (byte)x;
|
||||||
|
faceData.Y = (byte)y;
|
||||||
|
faceData.Z = (byte)z;
|
||||||
|
|
||||||
|
faces.Add(faceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block == Blocks.Air) continue; // ignore if air
|
||||||
|
|
||||||
|
int nx = x + Offsets[face].dx;
|
||||||
|
int ny = y + Offsets[face].dy;
|
||||||
|
int nz = z + Offsets[face].dz;
|
||||||
|
|
||||||
|
// check neighbor, ignore if at chunk edge
|
||||||
|
|
||||||
|
int ni = GetBlockIndex(nx, ny, nz);
|
||||||
|
if (GetBlockIndex(nx, ny, nz) == -1)
|
||||||
|
{
|
||||||
|
AddFace();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_blocks[ni] == Blocks.Air)
|
||||||
|
{
|
||||||
|
AddFace();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_chunkMesh.SetFaces(faces);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkMesh GetChunkMesh()
|
||||||
|
{
|
||||||
|
return _chunkMesh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
ChunkMesh.cs
Normal file
44
ChunkMesh.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public class ChunkMesh
|
||||||
|
{
|
||||||
|
public int X;
|
||||||
|
public int Y;
|
||||||
|
private byte[] _packedData;
|
||||||
|
public bool NeedsUpdate = false;
|
||||||
|
public int Size = 0;
|
||||||
|
private List<FaceData> _faces;
|
||||||
|
|
||||||
|
public ChunkMesh(int x, int y)
|
||||||
|
{
|
||||||
|
_packedData = new byte[0];
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFaces(List<FaceData> faces)
|
||||||
|
{
|
||||||
|
Size = faces.Count;
|
||||||
|
_faces = faces;
|
||||||
|
NeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetPackedData()
|
||||||
|
{
|
||||||
|
if (NeedsUpdate)
|
||||||
|
{
|
||||||
|
_packedData = _faces.SelectMany(f => f.Pack()).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _packedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
FaceData.cs
Normal file
26
FaceData.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public struct FaceData
|
||||||
|
{
|
||||||
|
public Orientation Facing;
|
||||||
|
public byte X, Y, Z;
|
||||||
|
public Textures Texture;
|
||||||
|
public byte LightLevel;
|
||||||
|
|
||||||
|
public byte[] Pack()
|
||||||
|
{
|
||||||
|
return new byte[]
|
||||||
|
{
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
Z,
|
||||||
|
(byte)Facing,
|
||||||
|
(byte)Texture,
|
||||||
|
LightLevel,
|
||||||
|
0,0 // two bits empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Input.cs
Normal file
19
Input.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public static class Input
|
||||||
|
{
|
||||||
|
private static Dictionary<Keys, bool> _keystates = new Dictionary<Keys, bool>();
|
||||||
|
|
||||||
|
public static bool GetKey(Keys key)
|
||||||
|
{
|
||||||
|
return _keystates.TryGetValue(key, out bool pressed) && pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetKey(Keys key, bool pressed)
|
||||||
|
{
|
||||||
|
_keystates[key] = pressed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Player.cs
Normal file
39
Player.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public class Player
|
||||||
|
{
|
||||||
|
public Vector3 Position;
|
||||||
|
|
||||||
|
private World _world;
|
||||||
|
|
||||||
|
public Player(World world, Vector3 startPos)
|
||||||
|
{
|
||||||
|
_world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BreakBlock()
|
||||||
|
{
|
||||||
|
var (success, hit, x, y, z) = _world.Raycast(Camera.Position, Camera.Front.Normalized(), 10);
|
||||||
|
if (!success) return;
|
||||||
|
|
||||||
|
_world.SetBlock(x, y, z, Blocks.Air);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
Camera.Update(deltaTime);
|
||||||
|
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.Space))
|
||||||
|
{
|
||||||
|
BreakBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Program.cs
22
Program.cs
@@ -1,4 +1,5 @@
|
|||||||
using Voxel;
|
using OpenTK.Mathematics;
|
||||||
|
using Voxel;
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,26 @@ internal class Program
|
|||||||
int sizeY = 600;
|
int sizeY = 600;
|
||||||
string title = "Game";
|
string title = "Game";
|
||||||
|
|
||||||
|
World world = new World();
|
||||||
Window window = new Window(sizeX, sizeY, title);
|
Window window = new Window(sizeX, sizeY, title);
|
||||||
|
Player player = new Player(world, Vector3.Zero);
|
||||||
|
|
||||||
|
window.Player = player;
|
||||||
|
|
||||||
|
for (int x = 0; x < 4; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < 4; y++)
|
||||||
|
{
|
||||||
|
Chunk chunk = new Chunk(x, y);
|
||||||
|
|
||||||
|
world.AddChunk(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// xmax ymin gör bugg
|
||||||
|
|
||||||
|
Renderer.SetWorld(world);
|
||||||
|
|
||||||
window.Run();
|
window.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
108
Renderer.cs
Normal file
108
Renderer.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
static class Renderer
|
||||||
|
{
|
||||||
|
private static int _ssbo;
|
||||||
|
private static int _vao;
|
||||||
|
private static bool _buffersDirty;
|
||||||
|
|
||||||
|
private static Dictionary<(int, int), int> _chunkBufferSizes = new Dictionary<(int, int), int>();
|
||||||
|
private static List<ChunkMesh> _chunkMeshes = new List<ChunkMesh>();
|
||||||
|
private static Shader _shader;
|
||||||
|
private static readonly Texture _texture;
|
||||||
|
private static World? _world;
|
||||||
|
|
||||||
|
static Renderer()
|
||||||
|
{
|
||||||
|
string vertexPath = "Shaders/shader.vert";
|
||||||
|
string fragmentPath = "Shaders/shader.frag";
|
||||||
|
string texturePath = "atlas.png";
|
||||||
|
|
||||||
|
_shader = new Shader(vertexPath, fragmentPath);
|
||||||
|
_texture = new Texture(texturePath);
|
||||||
|
|
||||||
|
_shader.SetInt("uTexture", 0);
|
||||||
|
|
||||||
|
_ssbo = GL.GenBuffer();
|
||||||
|
_vao = GL.GenVertexArray();
|
||||||
|
|
||||||
|
GL.BindVertexArray(_vao);
|
||||||
|
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo);
|
||||||
|
|
||||||
|
GL.BufferData(BufferTarget.ShaderStorageBuffer, 1024 * 1024 * 2, IntPtr.Zero, BufferUsageHint.DynamicDraw);
|
||||||
|
GL.BindBufferBase(BufferRangeTarget.ShaderStorageBuffer, 0, _ssbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Render()
|
||||||
|
{
|
||||||
|
GL.BindVertexArray(_vao);
|
||||||
|
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo);
|
||||||
|
|
||||||
|
_shader.Use();
|
||||||
|
_shader.SetMatrix4("view", Camera.view);
|
||||||
|
_shader.SetMatrix4("projection", Camera.projection);
|
||||||
|
|
||||||
|
if (_buffersDirty)
|
||||||
|
{
|
||||||
|
UpdateAllChunksBuffer();
|
||||||
|
_buffersDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateAllChunksBuffer()
|
||||||
|
{
|
||||||
|
if (_world == null) return;
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
foreach (Chunk chunk in _world.GetAllChunks())
|
||||||
|
{
|
||||||
|
ChunkMesh chunkMesh = chunk.GetChunkMesh();
|
||||||
|
|
||||||
|
if (chunkMesh.NeedsUpdate)
|
||||||
|
{
|
||||||
|
byte[] data = chunkMesh.GetPackedData();
|
||||||
|
GL.BufferSubData(BufferTarget.ShaderStorageBuffer, (IntPtr)offset * 8, chunkMesh.Size * 8, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_chunkBufferSizes[(chunk.X, chunk.Y)] = offset;
|
||||||
|
offset += chunkMesh.Size * 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenderWorld()
|
||||||
|
{
|
||||||
|
if (_world == null) return;
|
||||||
|
|
||||||
|
foreach (Chunk chunk in _world.GetAllChunks())
|
||||||
|
{
|
||||||
|
ChunkMesh chunkMesh = chunk.GetChunkMesh();
|
||||||
|
|
||||||
|
if (chunkMesh.Size == 0) continue;
|
||||||
|
|
||||||
|
if (!_chunkBufferSizes.TryGetValue((chunk.X, chunk.Y), out int offset)) continue;
|
||||||
|
|
||||||
|
_shader.SetInt("chunkX", chunk.X);
|
||||||
|
_shader.SetInt("chunkY", chunk.Y);
|
||||||
|
|
||||||
|
//GL.MemoryBarrier(MemoryBarrierFlags.ShaderStorageBarrierBit);
|
||||||
|
GL.DrawArrays(PrimitiveType.Triangles, offset * 6, chunkMesh.Size * 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetWorld(World world)
|
||||||
|
{
|
||||||
|
_world = world;
|
||||||
|
_buffersDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MarkBuffersDirty()
|
||||||
|
{
|
||||||
|
_buffersDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
Shader.cs
51
Shader.cs
@@ -1,16 +1,11 @@
|
|||||||
using OpenTK.Graphics.OpenGL4;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
using System;
|
using OpenTK.Mathematics;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Voxel
|
namespace Voxel
|
||||||
{
|
{
|
||||||
public class Shader
|
public class Shader
|
||||||
{
|
{
|
||||||
int handle;
|
private int _handle;
|
||||||
private bool disposedValue = false;
|
private bool disposedValue = false;
|
||||||
|
|
||||||
public Shader(string vertexPath, string fragmentPath)
|
public Shader(string vertexPath, string fragmentPath)
|
||||||
@@ -41,36 +36,58 @@ namespace Voxel
|
|||||||
|
|
||||||
// attach
|
// attach
|
||||||
|
|
||||||
handle = GL.CreateProgram();
|
_handle = GL.CreateProgram();
|
||||||
|
|
||||||
GL.AttachShader(handle, vertexShader);
|
GL.AttachShader(_handle, vertexShader);
|
||||||
GL.AttachShader(handle, fragmentShader);
|
GL.AttachShader(_handle, fragmentShader);
|
||||||
|
|
||||||
GL.LinkProgram(handle);
|
GL.LinkProgram(_handle);
|
||||||
|
|
||||||
GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out success);
|
GL.GetProgram(_handle, GetProgramParameterName.LinkStatus, out success);
|
||||||
if (success == 0)
|
if (success == 0)
|
||||||
{
|
{
|
||||||
string infoLog = GL.GetProgramInfoLog(handle);
|
string infoLog = GL.GetProgramInfoLog(_handle);
|
||||||
Console.WriteLine(infoLog);
|
Console.WriteLine(infoLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.DetachShader(handle, vertexShader);
|
GL.DetachShader(_handle, vertexShader);
|
||||||
GL.DetachShader(handle, fragmentShader);
|
GL.DetachShader(_handle, fragmentShader);
|
||||||
GL.DeleteShader(fragmentShader);
|
GL.DeleteShader(fragmentShader);
|
||||||
GL.DeleteShader(vertexShader);
|
GL.DeleteShader(vertexShader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetMatrix4(string name, Matrix4 matrix)
|
||||||
|
{
|
||||||
|
int location = GL.GetUniformLocation(_handle, name);
|
||||||
|
if (location == -1)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Uniform '{name}' not found in shader.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GL.UniformMatrix4(location, false, ref matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInt(string name, int value)
|
||||||
|
{
|
||||||
|
int location = GL.GetUniformLocation(_handle, name);
|
||||||
|
if (location == -1)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Uniform '{name}' not found in shader.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GL.Uniform1(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
public void Use()
|
public void Use()
|
||||||
{
|
{
|
||||||
GL.UseProgram(handle);
|
GL.UseProgram(_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposedValue)
|
if (!disposedValue)
|
||||||
{
|
{
|
||||||
GL.DeleteProgram(handle);
|
GL.DeleteProgram(_handle);
|
||||||
|
|
||||||
disposedValue = true;
|
disposedValue = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#version 330 core
|
#version 430 core
|
||||||
out vec4 FragColor;
|
|
||||||
|
|
||||||
in vec4 vertexColor;
|
out vec4 FragColor;
|
||||||
|
in vec2 fragUV;
|
||||||
|
in float lighting;
|
||||||
|
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
FragColor = vertexColor;
|
vec4 texColor = texture(uTexture, fragUV);
|
||||||
|
FragColor = vec4(texColor.rgb * lighting, texColor.a);
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,115 @@
|
|||||||
#version 330 core
|
#version 430 core
|
||||||
layout (location = 0) in vec3 aPosition;
|
|
||||||
|
|
||||||
out vec4 vertexColor;
|
struct FaceData {
|
||||||
|
uint x;
|
||||||
|
uint y;
|
||||||
|
uint z;
|
||||||
|
uint facing; // 0=+X,1=-X,2=+Y,3=-Y,4=+Z,5=-Z
|
||||||
|
uint texture;
|
||||||
|
uint lightLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(std430, binding = 0) buffer FaceBuffer {
|
||||||
|
uint faces[];
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform mat4 view;
|
||||||
|
uniform mat4 projection;
|
||||||
|
uniform int chunkX;
|
||||||
|
uniform int chunkY;
|
||||||
|
|
||||||
|
out vec2 fragUV;
|
||||||
|
out float lighting;
|
||||||
|
|
||||||
|
const float lightMult[6] = float[6](0.6, 0.6, 1.0, 0.5, 0.8, 0.8);
|
||||||
|
|
||||||
|
const vec3 offsets[6][6] = vec3[6][6](
|
||||||
|
vec3[6]( // +X
|
||||||
|
vec3(0.5, -0.5, -0.5),
|
||||||
|
vec3(0.5, 0.5, 0.5),
|
||||||
|
vec3(0.5, -0.5, 0.5),
|
||||||
|
vec3(0.5, 0.5, 0.5),
|
||||||
|
vec3(0.5, -0.5, -0.5),
|
||||||
|
vec3(0.5, 0.5, -0.5)
|
||||||
|
),
|
||||||
|
vec3[6]( // -X
|
||||||
|
vec3(-0.5, -0.5, 0.5),
|
||||||
|
vec3(-0.5, 0.5, -0.5),
|
||||||
|
vec3(-0.5, -0.5, -0.5),
|
||||||
|
vec3(-0.5, 0.5, -0.5),
|
||||||
|
vec3(-0.5, -0.5, 0.5),
|
||||||
|
vec3(-0.5, 0.5, 0.5)
|
||||||
|
),
|
||||||
|
vec3[6]( // +Y
|
||||||
|
vec3(-0.5, 0.5, -0.5),
|
||||||
|
vec3( 0.5, 0.5, 0.5),
|
||||||
|
vec3( 0.5, 0.5, -0.5),
|
||||||
|
vec3( 0.5, 0.5, 0.5),
|
||||||
|
vec3(-0.5, 0.5, -0.5),
|
||||||
|
vec3(-0.5, 0.5, 0.5)
|
||||||
|
),
|
||||||
|
vec3[6](
|
||||||
|
vec3(-0.5, -0.5, 0.5),
|
||||||
|
vec3( 0.5, -0.5, -0.5),
|
||||||
|
vec3( 0.5, -0.5, 0.5),
|
||||||
|
vec3( 0.5, -0.5, -0.5),
|
||||||
|
vec3(-0.5, -0.5, 0.5),
|
||||||
|
vec3(-0.5, -0.5, -0.5)
|
||||||
|
),
|
||||||
|
vec3[6]( // +Z
|
||||||
|
vec3( 0.5, -0.5, 0.5),
|
||||||
|
vec3(-0.5, 0.5, 0.5),
|
||||||
|
vec3(-0.5, -0.5, 0.5),
|
||||||
|
vec3(-0.5, 0.5, 0.5),
|
||||||
|
vec3( 0.5, -0.5, 0.5),
|
||||||
|
vec3( 0.5, 0.5, 0.5)
|
||||||
|
),
|
||||||
|
vec3[6]( // -Z
|
||||||
|
vec3(-0.5, -0.5, -0.5),
|
||||||
|
vec3( 0.5, 0.5, -0.5),
|
||||||
|
vec3( 0.5, -0.5, -0.5),
|
||||||
|
vec3( 0.5, 0.5, -0.5),
|
||||||
|
vec3(-0.5, -0.5, -0.5),
|
||||||
|
vec3(-0.5, 0.5, -0.5)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const vec2 uvs[6] = vec2[6](vec2(0,0), vec2(1,1), vec2(1,0), vec2(1,1), vec2(0,0), vec2(0,1));
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_Position = vec4(aPosition, 1.0);
|
uint faceIndex = gl_VertexID / 6u;
|
||||||
vertexColor = vec4(aPosition + vec3(0.5,0.5,0.5), 1.0);
|
uint vertIndex = gl_VertexID % 6u;
|
||||||
|
|
||||||
|
uint start = faceIndex * 2u; // 2 byte per face
|
||||||
|
|
||||||
|
uint u0 = faces[start]; // data in uint 0
|
||||||
|
uint u1 = faces[start + 1]; // data in uint 1
|
||||||
|
|
||||||
|
// extract values from bits
|
||||||
|
uint x = u0 & 0xFFu;
|
||||||
|
uint y = (u0 >> 8) & 0xFFu;
|
||||||
|
uint z = (u0 >> 16) & 0xFFu;
|
||||||
|
uint facing = (u0 >> 24) & 0xFFu;
|
||||||
|
uint texture = u1 & 0xFFu;
|
||||||
|
uint lightLevel = (u1 >> 8) & 0xFFu;
|
||||||
|
|
||||||
|
vec3 basePos = vec3(x + chunkX * 16, y, z + chunkY * 16);
|
||||||
|
vec4 worldPos = vec4(basePos + offsets[facing][vertIndex], 1.0);
|
||||||
|
float light = float(lightLevel) / 255.0; // use later for caves and stuff
|
||||||
|
|
||||||
|
// UV mapping
|
||||||
|
uint col = texture & 15u; // texture % 16
|
||||||
|
uint row = texture >> 4; // texture / 16
|
||||||
|
row = 15u - row; // invert row so 0 is top
|
||||||
|
|
||||||
|
// convert to float after int math, divide by 16
|
||||||
|
vec2 uv = uvs[vertIndex] * 0.0625;
|
||||||
|
uv.x += float(col) * 0.0625;
|
||||||
|
uv.y += float(row) * 0.0625;
|
||||||
|
|
||||||
|
fragUV = uv;
|
||||||
|
lighting = lightMult[facing];
|
||||||
|
|
||||||
|
gl_Position = projection * view * worldPos;
|
||||||
}
|
}
|
||||||
43
Texture.cs
Normal file
43
Texture.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
using StbImageSharp;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public class Texture
|
||||||
|
{
|
||||||
|
private int _handle;
|
||||||
|
private string _path;
|
||||||
|
|
||||||
|
public Texture(string path)
|
||||||
|
{
|
||||||
|
_handle = GL.GenTexture();
|
||||||
|
_path = path;
|
||||||
|
|
||||||
|
LoadFromFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFromFile()
|
||||||
|
{
|
||||||
|
StbImage.stbi_set_flip_vertically_on_load(1);
|
||||||
|
ImageResult image = ImageResult.FromStream(File.OpenRead(_path), ColorComponents.RedGreenBlueAlpha);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, _handle);
|
||||||
|
GL.TexImage2D(
|
||||||
|
TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
|
||||||
|
image.Width, image.Height, 0,
|
||||||
|
PixelFormat.Rgba, PixelType.UnsignedByte, image.Data
|
||||||
|
);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bind(TextureUnit unit = TextureUnit.Texture0)
|
||||||
|
{
|
||||||
|
GL.ActiveTexture(unit);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, _handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Textures.cs
Normal file
44
Textures.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public enum Textures : uint
|
||||||
|
{
|
||||||
|
GrassTop,
|
||||||
|
Stone,
|
||||||
|
Dirt,
|
||||||
|
GrassSide,
|
||||||
|
OakPlanks,
|
||||||
|
SmoothStoneSlab,
|
||||||
|
SmoothStone,
|
||||||
|
Bricks,
|
||||||
|
TntSide,
|
||||||
|
TntTop,
|
||||||
|
TntBottom,
|
||||||
|
Cobweb,
|
||||||
|
Rose,
|
||||||
|
Dandelion,
|
||||||
|
Water,
|
||||||
|
OakSapling,
|
||||||
|
Cobblestone,
|
||||||
|
Bedrock,
|
||||||
|
Sand,
|
||||||
|
Gravel,
|
||||||
|
OakSide,
|
||||||
|
OakTop,
|
||||||
|
IronBlock,
|
||||||
|
GoldBlock,
|
||||||
|
DiamondBlock,
|
||||||
|
EmeraldBlock,
|
||||||
|
RedstoneBlock,
|
||||||
|
none0,
|
||||||
|
RedMushroom,
|
||||||
|
BrownMushroom,
|
||||||
|
JungleSapling,
|
||||||
|
none1,
|
||||||
|
GoldOre,
|
||||||
|
IronOre,
|
||||||
|
CoalOre,
|
||||||
|
Bookshelf,
|
||||||
|
MossyCobblestone,
|
||||||
|
Obsidian,
|
||||||
|
}
|
||||||
|
}
|
||||||
48
Triangle.cs
48
Triangle.cs
@@ -1,48 +0,0 @@
|
|||||||
using OpenTK.Graphics.OpenGL4;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Voxel
|
|
||||||
{
|
|
||||||
|
|
||||||
public class Triangle
|
|
||||||
{
|
|
||||||
private int _vao;
|
|
||||||
private Shader _shader;
|
|
||||||
|
|
||||||
public Triangle()
|
|
||||||
{
|
|
||||||
string vertexPath = "Shaders/shader.vert";
|
|
||||||
string fragmentPath = "Shaders/shader.frag";
|
|
||||||
_shader = new Shader(vertexPath, fragmentPath);
|
|
||||||
|
|
||||||
float[] vertices =
|
|
||||||
{
|
|
||||||
-0.5f, -0.5f, 0.0f, //top
|
|
||||||
0.5f, -0.5f, 0.0f, // bottom right
|
|
||||||
0.0f, 0.5f, 0.0f // bottom left
|
|
||||||
};
|
|
||||||
|
|
||||||
_vao = GL.GenVertexArray();
|
|
||||||
GL.BindVertexArray(_vao);
|
|
||||||
|
|
||||||
int vbo = GL.GenBuffer();
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
|
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);
|
|
||||||
|
|
||||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
|
|
||||||
GL.EnableVertexAttribArray(0);
|
|
||||||
|
|
||||||
_shader.Use();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
GL.BindVertexArray(_vao);
|
|
||||||
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,13 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK" Version="4.9.4" />
|
<PackageReference Include="OpenTK" Version="4.9.4" />
|
||||||
|
<PackageReference Include="StbImageSharp" Version="2.30.15" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Update="atlas.png">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
<None Update="Shaders\shader.frag">
|
<None Update="Shaders\shader.frag">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
|||||||
79
Window.cs
79
Window.cs
@@ -1,30 +1,50 @@
|
|||||||
using OpenTK.Graphics.OpenGL4;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
using OpenTK.Windowing.Common;
|
using OpenTK.Windowing.Common;
|
||||||
|
using OpenTK.Windowing.Common.Input;
|
||||||
using OpenTK.Windowing.Desktop;
|
using OpenTK.Windowing.Desktop;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Voxel
|
namespace Voxel
|
||||||
{
|
{
|
||||||
public class Window(int width, int height, string title) : GameWindow(GameWindowSettings.Default, new NativeWindowSettings() { Size = (width, height), Title = title })
|
public class Window(int width, int height, string title) : GameWindow(GameWindowSettings.Default, new NativeWindowSettings() { ClientSize = (width, height), Title = title })
|
||||||
{
|
{
|
||||||
private Triangle _triangle;
|
public readonly int Width = width;
|
||||||
|
public readonly int Height = height;
|
||||||
|
public Player Player;
|
||||||
|
public uint frames = 0;
|
||||||
|
public double timeElapsed = 0;
|
||||||
|
|
||||||
protected override void OnUpdateFrame(FrameEventArgs args)
|
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnUpdateFrame(args);
|
base.OnUpdateFrame(e);
|
||||||
|
|
||||||
|
if (Input.GetKey(OpenTK.Windowing.GraphicsLibraryFramework.Keys.Escape))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Player != null)
|
||||||
|
{
|
||||||
|
Player.Update((float)e.Time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRenderFrame(FrameEventArgs args)
|
protected override void OnRenderFrame(FrameEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnRenderFrame(args);
|
base.OnRenderFrame(e);
|
||||||
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||||
|
|
||||||
_triangle.Draw();
|
frames++;
|
||||||
|
timeElapsed += e.Time;
|
||||||
|
if (timeElapsed >= 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("FPS: " + frames.ToString());
|
||||||
|
timeElapsed = 0;
|
||||||
|
frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.Update((float)e.Time);
|
||||||
|
Renderer.Render();
|
||||||
|
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
}
|
}
|
||||||
@@ -33,6 +53,8 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
base.OnFramebufferResize(e);
|
base.OnFramebufferResize(e);
|
||||||
|
|
||||||
|
Camera.UpdateProjection(e.Width, e.Height);
|
||||||
|
|
||||||
GL.Viewport(0, 0, e.Width, e.Height);
|
GL.Viewport(0, 0, e.Width, e.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,9 +62,36 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
base.OnLoad();
|
base.OnLoad();
|
||||||
|
|
||||||
_triangle = new Triangle();
|
GL.ClearColor(0.72f, 0.88f, 0.97f, 1f);
|
||||||
|
|
||||||
GL.ClearColor(0.5f, 0.5f, 0.5f, 1f);
|
GL.Enable(EnableCap.CullFace);
|
||||||
|
GL.Enable(EnableCap.DepthTest);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
||||||
|
|
||||||
|
CursorState = CursorState.Grabbed;
|
||||||
|
VSync = VSyncMode.On;
|
||||||
|
|
||||||
|
Camera.UpdateProjection(Width, Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseMove(MouseMoveEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnMouseMove(e);
|
||||||
|
|
||||||
|
Camera.UpdateMouse(e.Delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyUp(KeyboardKeyEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnKeyUp(e);
|
||||||
|
Input.SetKey(e.Key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyDown(KeyboardKeyEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
Input.SetKey(e.Key, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
World.cs
Normal file
137
World.cs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Voxel
|
||||||
|
{
|
||||||
|
public class World
|
||||||
|
{
|
||||||
|
private Dictionary<(int, int), Chunk> _chunks;
|
||||||
|
|
||||||
|
public World()
|
||||||
|
{
|
||||||
|
_chunks = new Dictionary<(int, int), Chunk>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk GetChunk(int chunkX, int chunkZ)
|
||||||
|
{
|
||||||
|
_chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk);
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddChunk(Chunk chunk)
|
||||||
|
{
|
||||||
|
_chunks[(chunk.X, chunk.Y)] = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveChunk(int chunkX, int chunkZ)
|
||||||
|
{
|
||||||
|
_chunks.Remove((chunkX, chunkZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Chunk> GetAllChunks()
|
||||||
|
{
|
||||||
|
return _chunks.Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Blocks GetBlock(int worldX, int worldY, int worldZ)
|
||||||
|
{
|
||||||
|
int chunkX = worldX / Chunk.Size;
|
||||||
|
int chunkZ = worldZ / Chunk.Size;
|
||||||
|
Chunk chunk = GetChunk(chunkX, chunkZ);
|
||||||
|
if (chunk == null) return 0; // air if chunk not loaded
|
||||||
|
|
||||||
|
int localX = worldX % Chunk.Size;
|
||||||
|
int localZ = worldZ % Chunk.Size;
|
||||||
|
|
||||||
|
return chunk.GetBlock(localX, worldY, localZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
|
||||||
|
{
|
||||||
|
int chunkX = worldX / Chunk.Size;
|
||||||
|
int chunkZ = worldZ / Chunk.Size;
|
||||||
|
Chunk chunk = GetChunk(chunkX, chunkZ);
|
||||||
|
if (chunk == null) return;
|
||||||
|
|
||||||
|
int localX = worldX % Chunk.Size;
|
||||||
|
int localZ = worldZ % Chunk.Size;
|
||||||
|
|
||||||
|
chunk.SetBlock(localX, worldY, localZ, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public (bool success, Blocks block, int x, int y, int z) Raycast(Vector3 origin, Vector3 direction, float maxDistance)
|
||||||
|
{
|
||||||
|
int x = (int)MathF.Floor(origin.X);
|
||||||
|
int y = (int)MathF.Floor(origin.Y);
|
||||||
|
int z = (int)MathF.Floor(origin.Z);
|
||||||
|
|
||||||
|
int stepX = direction.X > 0 ? 1 : -1;
|
||||||
|
int stepY = direction.Y > 0 ? 1 : -1;
|
||||||
|
int stepZ = direction.Z > 0 ? 1 : -1;
|
||||||
|
|
||||||
|
float tDeltaX = direction.X != 0 ? MathF.Abs(1 / direction.X) : float.MaxValue;
|
||||||
|
float tDeltaY = direction.Y != 0 ? MathF.Abs(1 / direction.Y) : float.MaxValue;
|
||||||
|
float tDeltaZ = direction.Z != 0 ? MathF.Abs(1 / direction.Z) : float.MaxValue;
|
||||||
|
|
||||||
|
float tMaxX = direction.X > 0
|
||||||
|
? (MathF.Floor(origin.X) + 1 - origin.X) * tDeltaX
|
||||||
|
: (origin.X - MathF.Floor(origin.X)) * tDeltaX;
|
||||||
|
float tMaxY = direction.Y > 0
|
||||||
|
? (MathF.Floor(origin.Y) + 1 - origin.Y) * tDeltaY
|
||||||
|
: (origin.Y - MathF.Floor(origin.Y)) * tDeltaY;
|
||||||
|
float tMaxZ = direction.Z > 0
|
||||||
|
? (MathF.Floor(origin.Z) + 1 - origin.Z) * tDeltaZ
|
||||||
|
: (origin.Z - MathF.Floor(origin.Z)) * tDeltaZ;
|
||||||
|
|
||||||
|
float distance = 0f;
|
||||||
|
|
||||||
|
while (distance <= maxDistance)
|
||||||
|
{
|
||||||
|
Blocks block = GetBlock(x, y, z);
|
||||||
|
if (block != Blocks.Air)
|
||||||
|
{
|
||||||
|
return (true, block, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// step to next voxel
|
||||||
|
if (tMaxX < tMaxY)
|
||||||
|
{
|
||||||
|
if (tMaxX < tMaxZ)
|
||||||
|
{
|
||||||
|
x += stepX;
|
||||||
|
distance = tMaxX;
|
||||||
|
tMaxX += tDeltaX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
z += stepZ;
|
||||||
|
distance = tMaxZ;
|
||||||
|
tMaxZ += tDeltaZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tMaxY < tMaxZ)
|
||||||
|
{
|
||||||
|
y += stepY;
|
||||||
|
distance = tMaxY;
|
||||||
|
tMaxY += tDeltaY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
z += stepZ;
|
||||||
|
distance = tMaxZ;
|
||||||
|
tMaxZ += tDeltaZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (false, Blocks.Air, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user