From 623ada4b2f0d35f479aa5209f7b3388d8771d93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Thu, 4 Mar 2021 13:05:57 +0100 Subject: [PATCH] Added chunk editing --- Sandbox/src/VoxelFun/ChunkManager.bf | 100 +++++++++++++++++++++---- Sandbox/src/VoxelFun/VoxelTestLayer.bf | 32 ++++++++ Sandbox/src/VoxelFun/World.bf | 39 ++++++++-- 3 files changed, 148 insertions(+), 23 deletions(-) diff --git a/Sandbox/src/VoxelFun/ChunkManager.bf b/Sandbox/src/VoxelFun/ChunkManager.bf index 8d0e508..4c58ff1 100644 --- a/Sandbox/src/VoxelFun/ChunkManager.bf +++ b/Sandbox/src/VoxelFun/ChunkManager.bf @@ -15,10 +15,48 @@ namespace Sandbox.VoxelFun { public class Chunk { + public ChunkManager ChunkManager; public GeometryBinding Geometry ~ _?.ReleaseRef(); + public VoxelChunk Data; public Matrix Transform; public Int32_3 Position; + public Int32_3 Coordinate; + + private bool _isDirty; + private bool _isGeometryDirty; + + public bool IsDirty => _isDirty; + + public bool IsGeometryDirty + { + get => _isGeometryDirty; + set => _isGeometryDirty = value; + } + + public void SetBlock(Int32_3 coordinate, uint8 blockId) + { + Log.EngineLogger.AssertDebug(coordinate.X >= 0 && coordinate.Y >= 0 && coordinate.Z >= 0 && coordinate.X < VoxelChunk.Size.X && coordinate.Y < VoxelChunk.Size.Y && coordinate.Z < VoxelChunk.Size.Z, "Specified coordinate is out of range."); + + Data.Data[coordinate.X][coordinate.Y][coordinate.Z] = blockId; + + _isDirty = true; + _isGeometryDirty = true; + ChunkManager.[Friend]chunkPosLock.Exit(); + } + + public uint8 GetBlock(Int32_3 coordinate) + { + Log.EngineLogger.AssertDebug(coordinate.X >= 0 && coordinate.Y >= 0 && coordinate.Z >= 0 && coordinate.X < VoxelChunk.Size.X && coordinate.Y < VoxelChunk.Size.Y && coordinate.Z < VoxelChunk.Size.Z, "Specified coordinate is out of range."); + + return Data.Data[coordinate.X][coordinate.Y][coordinate.Z]; + } + + public void Serialize(Stream stream) + { + stream.Write(Data); + _isDirty = false; + } } public class ChunkManager @@ -30,7 +68,7 @@ namespace Sandbox.VoxelFun private String _chunkBasePath ~ delete _; - Dictionary _chunks = new .() ~ DeleteDictionaryAndValues!(_); + Dictionary _chunks = new .() ~ delete _; private HashSet _generatingChunks = new .() ~ delete _; private Monitor _generatingChunksLock = new .() ~ delete _; @@ -83,6 +121,12 @@ namespace Sandbox.VoxelFun stopChunkLoader = true; chunkLoader.Join(); + + // Unload (and save) all chunks + for(var pair in _chunks) + { + UnloadChunk(pair.value); + } } /** @@ -133,13 +177,15 @@ namespace Sandbox.VoxelFun if(!alreadyInList) { chunk = new Chunk(); + chunk.ChunkManager = this; + chunk.Coordinate = chunkCoordinate; chunk.Position = chunkCoordinate * .(VoxelChunk.SizeX, VoxelChunk.SizeY, VoxelChunk.SizeZ); chunk.Transform = .Translation(chunk.Position.X, chunk.Position.Y, chunk.Position.Z); if(LoadChunkFromFile(chunkCoordinate, chunk) case .Err) { GenTestChunk(chunk); - SaveChunkToFile(chunkCoordinate, chunk); + SaveChunkToFile(chunk); } GenChunkGeo(chunk); @@ -243,8 +289,8 @@ namespace Sandbox.VoxelFun } // Position didn't change -> skip - if(curChunkPosition == oldChunkPosition) - continue; + //if(curChunkPosition == oldChunkPosition) + // continue; ChunkLoaderThread_GenerateChunks(curChunkPosition); @@ -252,6 +298,20 @@ namespace Sandbox.VoxelFun } } + private void UnloadChunk(Chunk chunk) + { + if(chunk.IsDirty) + { + SaveChunkToFile(chunk); + } + + using(chunkListLock.Enter()) + { + _chunks.Remove(chunk.Coordinate); + delete chunk; + } + } + public void ChunkLoaderThread_GenerateChunks(Int32_3 chunkPos) { for(var chunk in _chunks) @@ -260,11 +320,11 @@ namespace Sandbox.VoxelFun if(dist.X > _viewDistance || dist.Z > _viewDistance) { - using(chunkListLock.Enter()) - { - delete chunk.value; - _chunks.Remove(chunk.key); - } + UnloadChunk(chunk.value); + } + else if(chunk.value.IsGeometryDirty) + { + GenChunkGeo(chunk.value); } } @@ -313,16 +373,16 @@ namespace Sandbox.VoxelFun return .Ok; } - Result SaveChunkToFile(Int32_3 chunkCoordinate, Chunk outChunk) + Result SaveChunkToFile(Chunk chunk) { String chunkFileName = scope String(_chunkBasePath); - chunkFileName.AppendF($"{chunkCoordinate.X}_{chunkCoordinate.Y}_{chunkCoordinate.Z}.cdata"); + chunkFileName.AppendF($"{chunk.Coordinate.X}_{chunk.Coordinate.Y}_{chunk.Coordinate.Z}.cdata"); FileStream fs = scope FileStream(); fs.Open(chunkFileName, FileMode.Create, .Write); - fs.Write(outChunk.Data); + chunk.Serialize(fs); fs.Close(); @@ -463,8 +523,14 @@ namespace Sandbox.VoxelFun void GenChunkGeo(Chunk chunk) { - chunk.Geometry?.ReleaseRef(); - chunk.Geometry = voxelGeoGen.GenerateGeometry(chunk.Data); + var oldGeometry = chunk.Geometry; + + var newGeometry = voxelGeoGen.GenerateGeometry(chunk.Data); + + Interlocked.Store(ref chunk.Geometry, newGeometry); + chunk.IsGeometryDirty = false; + + oldGeometry?.ReleaseRef(); } public void Draw() @@ -474,12 +540,16 @@ namespace Sandbox.VoxelFun { Chunk chunk = pair.value; - if(chunk?.Geometry == null) + GeometryBinding chunkGeo = Interlocked.Load(ref chunk.Geometry); + chunkGeo.AddRef(); + + if(chunkGeo == null) continue; Texture.Bind(); Renderer.Submit(chunk.Geometry, TextureEffect, chunk.Transform); + chunkGeo.ReleaseRef(); } chunkListLock.Exit(); } diff --git a/Sandbox/src/VoxelFun/VoxelTestLayer.bf b/Sandbox/src/VoxelFun/VoxelTestLayer.bf index 78261b7..1872acd 100644 --- a/Sandbox/src/VoxelFun/VoxelTestLayer.bf +++ b/Sandbox/src/VoxelFun/VoxelTestLayer.bf @@ -398,6 +398,38 @@ namespace Sandbox.VoxelFun Renderer.Submit(_cubeGeo, _textureEffect, .Translation(intersectInfo.Location) * .Scaling(0.1f) * .Translation(-0.5f.XXX)); + if(intersectInfo.Face != .None) + { + if(Input.IsMouseButtonPressing(.LeftButton)) + { + _world.SetBlock(intersectInfo.Coordinate, 0); + } + else if(Input.IsMouseButtonPressing(.RightButton)) + { + var newBlockCoord = intersectInfo.Coordinate; + + switch(intersectInfo.Face) + { + case .Front: + newBlockCoord.Z--; + case .Back: + newBlockCoord.Z++; + case .Left: + newBlockCoord.X--; + case .Right: + newBlockCoord.X++; + case .Bottom: + newBlockCoord.Y--; + case .Top: + newBlockCoord.Y++; + default: + Log.ClientLogger.Error("Unknown block face."); + } + + _world.SetBlock(newBlockCoord, 1); + } + } + _world.ChunkManager.Draw(); Renderer.EndScene(); diff --git a/Sandbox/src/VoxelFun/World.bf b/Sandbox/src/VoxelFun/World.bf index 007028e..8bd8307 100644 --- a/Sandbox/src/VoxelFun/World.bf +++ b/Sandbox/src/VoxelFun/World.bf @@ -280,13 +280,7 @@ namespace Sandbox.VoxelFun } } - Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(walker); - - Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate); - - Int32_3 coordInChunk = BlockCoordInChunk(walker); - - uint8 blockData = chunk.Data.Data[coordInChunk.X][coordInChunk.Y][coordInChunk.Z]; + uint8 blockData = [Inline]GetBlock(walker); if(blockData != 0) { @@ -294,12 +288,41 @@ namespace Sandbox.VoxelFun return walker; } - } intersectionFace = .None; intersectionPosition = .(float.NaN); return .(int32.MaxValue, int32.MaxValue, int32.MaxValue); } + + /** + * Gets the block at the specified coordinate. + * @remarks If the blocks chunk is not loaded it will be loaded from the disk. + */ + public void SetBlock(Int32_3 blockCoordinate, uint8 blockId) + { + Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(blockCoordinate); + + Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate); + + Int32_3 coordInChunk = BlockCoordInChunk(blockCoordinate); + + chunk.SetBlock(coordInChunk, blockId); + } + + /** + * Gets the block at the specified coordinate. + * @remarks If the blocks chunk is not loaded it will be loaded from the disk. + */ + public uint8 GetBlock(Int32_3 blockCoordinate) + { + Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(blockCoordinate); + + Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate); + + Int32_3 coordInChunk = BlockCoordInChunk(blockCoordinate); + + return chunk.GetBlock(coordInChunk); + } } }