update
This commit is contained in:
62
Chunk.cs
62
Chunk.cs
@@ -6,6 +6,7 @@
|
|||||||
public static int Height = 256;
|
public static int Height = 256;
|
||||||
public readonly int X;
|
public readonly int X;
|
||||||
public readonly int Y;
|
public readonly int Y;
|
||||||
|
private ChunkMesh _chunkMesh;
|
||||||
|
|
||||||
private Dictionary<ushort, BlockData> _blockData;
|
private Dictionary<ushort, BlockData> _blockData;
|
||||||
private Blocks[] _blocks;
|
private Blocks[] _blocks;
|
||||||
@@ -17,14 +18,18 @@
|
|||||||
|
|
||||||
_blockData = new Dictionary<ushort, BlockData>();
|
_blockData = new Dictionary<ushort, BlockData>();
|
||||||
_blocks = new Blocks[Size * Size * Height];
|
_blocks = new Blocks[Size * Size * Height];
|
||||||
|
_chunkMesh = new ChunkMesh(X, Y);
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBlock(int x, int y, int z, Blocks block)
|
public void SetBlock(int x, int y, int z, Blocks block)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(x.ToString() + ", " + y.ToString());
|
||||||
int i = GetBlockIndex(x, y, z);
|
int i = GetBlockIndex(x, y, z);
|
||||||
|
if (i == -1) return;
|
||||||
_blocks[i] = block;
|
_blocks[i] = block;
|
||||||
|
UpdateChunkMesh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBlockIndex(int i, Blocks block)
|
public void SetBlockIndex(int i, Blocks block)
|
||||||
@@ -35,6 +40,7 @@
|
|||||||
public Blocks GetBlock(int x, int y, int z)
|
public Blocks GetBlock(int x, int y, int z)
|
||||||
{
|
{
|
||||||
int i = GetBlockIndex(x, y, z);
|
int i = GetBlockIndex(x, y, z);
|
||||||
|
if (i == -1) return Blocks.Air;
|
||||||
return _blocks[i];
|
return _blocks[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +57,7 @@
|
|||||||
_blocks[i] = Blocks.Stone;
|
_blocks[i] = Blocks.Stone;
|
||||||
for (int i = Size * Size * 15; i < Size * Size * 16; i++)
|
for (int i = Size * Size * 15; i < Size * Size * 16; i++)
|
||||||
_blocks[i] = Blocks.Grass;
|
_blocks[i] = Blocks.Grass;
|
||||||
|
UpdateChunkMesh();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
@@ -66,7 +73,7 @@
|
|||||||
private int GetBlockIndex(int x, int y, int z)
|
private int GetBlockIndex(int x, int y, int z)
|
||||||
{
|
{
|
||||||
if (x < 0 || x > 15 || y < 0 || y > 255 || z < 0 || z > 15)
|
if (x < 0 || x > 15 || y < 0 || y > 255 || z < 0 || z > 15)
|
||||||
return 0;
|
return -1;
|
||||||
|
|
||||||
return x + z * Size + y * Size * Size;
|
return x + z * Size + y * Size * Size;
|
||||||
}
|
}
|
||||||
@@ -81,14 +88,10 @@
|
|||||||
( 0, 0, -1) // -Z
|
( 0, 0, -1) // -Z
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void UpdateChunkMesh()
|
||||||
public ChunkMesh GetChunkMesh()
|
|
||||||
{
|
{
|
||||||
ChunkMesh chunkMesh = new ChunkMesh(X, Y);
|
|
||||||
List<FaceData> faces = new List<FaceData>(Size * Size * Height / 2);
|
List<FaceData> faces = new List<FaceData>(Size * Size * Height / 2);
|
||||||
|
|
||||||
// offsets table
|
|
||||||
|
|
||||||
for (int x = 0; x < Size; x++)
|
for (int x = 0; x < Size; x++)
|
||||||
{
|
{
|
||||||
for (int z = 0; z < Size; z++)
|
for (int z = 0; z < Size; z++)
|
||||||
@@ -100,6 +103,20 @@
|
|||||||
int indexBase = y * Size * Size + z * Size + x;
|
int indexBase = y * Size * Size + z * Size + x;
|
||||||
Blocks block = _blocks[indexBase];
|
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
|
if (block == Blocks.Air) continue; // ignore if air
|
||||||
|
|
||||||
int nx = x + Offsets[face].dx;
|
int nx = x + Offsets[face].dx;
|
||||||
@@ -107,30 +124,29 @@
|
|||||||
int nz = z + Offsets[face].dz;
|
int nz = z + Offsets[face].dz;
|
||||||
|
|
||||||
// check neighbor, ignore if at chunk edge
|
// check neighbor, ignore if at chunk edge
|
||||||
if (nx >= 0 && nx < Size &&
|
|
||||||
ny >= 0 && ny < Height &&
|
int ni = GetBlockIndex(nx, ny, nz);
|
||||||
nz >= 0 && nz < Size &&
|
if (GetBlockIndex(nx, ny, nz) == -1)
|
||||||
_blocks[GetBlockIndex(nx, ny, nz)] != 0)
|
{
|
||||||
|
AddFace();
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
FaceData faceData = new FaceData();
|
if (_blocks[ni] == Blocks.Air)
|
||||||
|
{
|
||||||
faceData.Facing = (Orientation)face;
|
AddFace();
|
||||||
faceData.Texture = BlockDefinitions.Blocks[block].FaceTextures[face];
|
continue;
|
||||||
|
}
|
||||||
faceData.X = (byte)x;
|
|
||||||
faceData.Y = (byte)y;
|
|
||||||
faceData.Z = (byte)z;
|
|
||||||
|
|
||||||
faces.Add(faceData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_chunkMesh.SetFaces(faces);
|
||||||
|
}
|
||||||
|
|
||||||
chunkMesh.SetFaces(faces);
|
public ChunkMesh GetChunkMesh()
|
||||||
|
{
|
||||||
return chunkMesh;
|
return _chunkMesh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
ChunkMesh.cs
25
ChunkMesh.cs
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using OpenTK.Graphics.OpenGL4;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -10,22 +11,36 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
public int X;
|
public int X;
|
||||||
public int Y;
|
public int Y;
|
||||||
public byte[] PackedData;
|
private byte[] _packedData;
|
||||||
public bool NeedsUpdate = true;
|
public bool NeedsUpdate = false;
|
||||||
public int Length = 0;
|
public int Length = 0;
|
||||||
|
private List<FaceData> _faces;
|
||||||
|
public int SSBO;
|
||||||
|
|
||||||
public ChunkMesh(int x, int y)
|
public ChunkMesh(int x, int y)
|
||||||
{
|
{
|
||||||
PackedData = new byte[0];
|
_packedData = new byte[0];
|
||||||
X = x;
|
X = x;
|
||||||
Y = y;
|
Y = y;
|
||||||
|
SSBO = GL.GenBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFaces(List<FaceData> faces)
|
public void SetFaces(List<FaceData> faces)
|
||||||
{
|
{
|
||||||
Length = faces.Count;
|
Length = faces.Count;
|
||||||
PackedData = faces.SelectMany(f => f.Pack()).ToArray();
|
_faces = faces;
|
||||||
NeedsUpdate = true;
|
NeedsUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] GetPackedData()
|
||||||
|
{
|
||||||
|
if (NeedsUpdate)
|
||||||
|
{
|
||||||
|
_packedData = _faces.SelectMany(f => f.Pack()).ToArray();
|
||||||
|
NeedsUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _packedData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,9 @@ namespace Voxel
|
|||||||
|
|
||||||
public void BreakBlock()
|
public void BreakBlock()
|
||||||
{
|
{
|
||||||
var (hit, x, y, z) = _world.Raycast(5f); // max 5 blocks
|
var (success, hit, x, y, z) = _world.Raycast(Camera.Position, Camera.Front * 10, 100);
|
||||||
if (hit != Blocks.Air)
|
if (!success) return;
|
||||||
{
|
_world.SetBlock(x, y, z, Blocks.Air);
|
||||||
_world.SetBlock(x, y, z, Blocks.Air);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(float deltaTime)
|
public void Update(float deltaTime)
|
||||||
|
|||||||
15
Program.cs
15
Program.cs
@@ -15,14 +15,17 @@ internal class Program
|
|||||||
|
|
||||||
window.Player = player;
|
window.Player = player;
|
||||||
|
|
||||||
Chunk chunk0 = new Chunk(0, 0);
|
for (int x = 0; x < 2; x++)
|
||||||
Chunk chunk1 = new Chunk(0, 1);
|
{
|
||||||
|
for (int y = 0; y < 2; y++)
|
||||||
|
{
|
||||||
|
Chunk chunk = new Chunk(1+ x, 1 +y);
|
||||||
|
|
||||||
world.AddChunk(chunk0);
|
world.AddChunk(chunk);
|
||||||
world.AddChunk(chunk1);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Renderer.AddChunkMesh(chunk0.GetChunkMesh());
|
Renderer.SetWorld(world);
|
||||||
Renderer.AddChunkMesh(chunk1.GetChunkMesh());
|
|
||||||
|
|
||||||
window.Run();
|
window.Run();
|
||||||
}
|
}
|
||||||
|
|||||||
39
Renderer.cs
39
Renderer.cs
@@ -9,7 +9,7 @@ namespace Voxel
|
|||||||
private static List<ChunkMesh> _chunkMeshes = new List<ChunkMesh>();
|
private static List<ChunkMesh> _chunkMeshes = new List<ChunkMesh>();
|
||||||
private static Shader _shader;
|
private static Shader _shader;
|
||||||
private static Texture _texture;
|
private static Texture _texture;
|
||||||
private static bool _needsUpdate = false;
|
private static World _world;
|
||||||
|
|
||||||
static Renderer()
|
static Renderer()
|
||||||
{
|
{
|
||||||
@@ -35,32 +35,31 @@ namespace Voxel
|
|||||||
GL.BindVertexArray(_vao);
|
GL.BindVertexArray(_vao);
|
||||||
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo);
|
GL.BindBuffer(BufferTarget.ShaderStorageBuffer, _ssbo);
|
||||||
|
|
||||||
//if (_needsUpdate)
|
|
||||||
//{
|
|
||||||
// _needsUpdate = false;
|
|
||||||
// byte[] data = _faces.SelectMany(f => f.Pack()).ToArray();
|
|
||||||
// GL.BufferData(BufferTarget.ShaderStorageBuffer, data.Length, data, BufferUsageHint.StaticRead);
|
|
||||||
//}
|
|
||||||
|
|
||||||
_shader.Use();
|
_shader.Use();
|
||||||
_shader.SetMatrix4("view", Camera.view);
|
_shader.SetMatrix4("view", Camera.view);
|
||||||
_shader.SetMatrix4("projection", Camera.projection);
|
_shader.SetMatrix4("projection", Camera.projection);
|
||||||
|
|
||||||
for (int i = 0; i < _chunkMeshes.Count; i++)
|
RenderWorld();
|
||||||
{
|
|
||||||
ChunkMesh chunkMesh = _chunkMeshes[i];
|
|
||||||
_shader.SetInt("chunkX", chunkMesh.X);
|
|
||||||
_shader.SetInt("chunkY", chunkMesh.Y);
|
|
||||||
GL.BufferData(BufferTarget.ShaderStorageBuffer, chunkMesh.PackedData.Length, chunkMesh.PackedData, BufferUsageHint.StaticRead);
|
|
||||||
GL.DrawArrays(PrimitiveType.Triangles, 0, chunkMesh.Length * 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Console.WriteLine("Rendered " + _faces.Count.ToString() + " faces");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddChunkMesh(ChunkMesh chunkMesh)
|
private static void RenderWorld()
|
||||||
{
|
{
|
||||||
_chunkMeshes.Add(chunkMesh);
|
if (_world == null) return;
|
||||||
|
|
||||||
|
foreach (Chunk chunk in _world.GetAllChunks())
|
||||||
|
{
|
||||||
|
ChunkMesh chunkMesh = chunk.GetChunkMesh();
|
||||||
|
byte[] data = chunkMesh.GetPackedData();
|
||||||
|
_shader.SetInt("chunkX", chunk.X);
|
||||||
|
_shader.SetInt("chunkY", chunk.Y);
|
||||||
|
GL.BufferData(BufferTarget.ShaderStorageBuffer, chunkMesh.Length * 8, data, BufferUsageHint.StaticRead);
|
||||||
|
GL.DrawArrays(PrimitiveType.Triangles, 0, chunkMesh.Length * 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetWorld(World world)
|
||||||
|
{
|
||||||
|
_world = world;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
80
World.cs
80
World.cs
@@ -12,32 +12,27 @@ namespace Voxel
|
|||||||
_chunks = new Dictionary<(int, int), Chunk>();
|
_chunks = new Dictionary<(int, int), Chunk>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a chunk at world coordinates, returns null if not loaded
|
|
||||||
public Chunk GetChunk(int chunkX, int chunkZ)
|
public Chunk GetChunk(int chunkX, int chunkZ)
|
||||||
{
|
{
|
||||||
_chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk);
|
_chunks.TryGetValue((chunkX, chunkZ), out Chunk chunk);
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a chunk
|
|
||||||
public void AddChunk(Chunk chunk)
|
public void AddChunk(Chunk chunk)
|
||||||
{
|
{
|
||||||
_chunks[(chunk.X, chunk.Y)] = chunk;
|
_chunks[(chunk.X, chunk.Y)] = chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a chunk
|
|
||||||
public void RemoveChunk(int chunkX, int chunkZ)
|
public void RemoveChunk(int chunkX, int chunkZ)
|
||||||
{
|
{
|
||||||
_chunks.Remove((chunkX, chunkZ));
|
_chunks.Remove((chunkX, chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over all chunks
|
|
||||||
public IEnumerable<Chunk> GetAllChunks()
|
public IEnumerable<Chunk> GetAllChunks()
|
||||||
{
|
{
|
||||||
return _chunks.Values;
|
return _chunks.Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: get a block at world coordinates
|
|
||||||
public Blocks GetBlock(int worldX, int worldY, int worldZ)
|
public Blocks GetBlock(int worldX, int worldY, int worldZ)
|
||||||
{
|
{
|
||||||
int chunkX = worldX / Chunk.Size;
|
int chunkX = worldX / Chunk.Size;
|
||||||
@@ -51,7 +46,6 @@ namespace Voxel
|
|||||||
return chunk.GetBlock(localX, worldY, localZ);
|
return chunk.GetBlock(localX, worldY, localZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: set a block at world coordinates
|
|
||||||
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
|
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
|
||||||
{
|
{
|
||||||
int chunkX = worldX / Chunk.Size;
|
int chunkX = worldX / Chunk.Size;
|
||||||
@@ -65,32 +59,74 @@ namespace Voxel
|
|||||||
chunk.SetBlock(localX, worldY, localZ, block);
|
chunk.SetBlock(localX, worldY, localZ, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (Blocks block, int x, int y, int z) Raycast(float length)
|
public (bool success, Blocks block, int x, int y, int z) Raycast(Vector3 origin, Vector3 direction, float maxDistance)
|
||||||
{
|
{
|
||||||
Vector3 start = Camera.Position;
|
int x = (int)MathF.Floor(origin.X);
|
||||||
Vector3 dir = Camera.Front.Normalized();
|
int y = (int)MathF.Floor(origin.Y);
|
||||||
|
int z = (int)MathF.Floor(origin.Z);
|
||||||
|
|
||||||
float stepSize = 0.01f;
|
int stepX = direction.X > 0 ? 1 : -1;
|
||||||
float distanceTraveled = 0f;
|
int stepY = direction.Y > 0 ? 1 : -1;
|
||||||
|
int stepZ = direction.Z > 0 ? 1 : -1;
|
||||||
|
|
||||||
while (distanceTraveled < length)
|
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)
|
||||||
{
|
{
|
||||||
Vector3 point = start + dir * distanceTraveled;
|
Blocks block = GetBlock(x, y, z);
|
||||||
|
|
||||||
int bx = (int)MathF.Floor(point.X);
|
|
||||||
int by = (int)MathF.Floor(point.Y);
|
|
||||||
int bz = (int)MathF.Floor(point.Z);
|
|
||||||
|
|
||||||
Blocks block = GetBlock(bx, by, bz);
|
|
||||||
if (block != Blocks.Air)
|
if (block != Blocks.Air)
|
||||||
{
|
{
|
||||||
return (block, bx, by, bz);
|
return (true, block, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
distanceTraveled += stepSize;
|
// 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 (Blocks.Air, 0, 0, 0);
|
return (false, Blocks.Air, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
|
|||||||
Reference in New Issue
Block a user