From 2d0ce96cc04eea2786d06cf265bdd8cd2ee46787 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 20 Sep 2015 15:49:31 -0400 Subject: [PATCH] Avoid rendering unseen blocks at chunk boundaries --- TrueCraft.API/Coordinates2D.cs | 20 +++ TrueCraft.Client/Rendering/BlockRenderer.cs | 7 +- TrueCraft.Client/Rendering/ChunkRenderer.cs | 177 ++++++++++++++++---- TrueCraft.Client/TrueCraft.Client.csproj | 10 +- TrueCraft.Client/TrueCraftGame.cs | 4 +- 5 files changed, 176 insertions(+), 42 deletions(-) diff --git a/TrueCraft.API/Coordinates2D.cs b/TrueCraft.API/Coordinates2D.cs index 196f758..943002a 100644 --- a/TrueCraft.API/Coordinates2D.cs +++ b/TrueCraft.API/Coordinates2D.cs @@ -250,6 +250,26 @@ namespace TrueCraft.API /// public static readonly Coordinates2D Right = new Coordinates2D(1, 0); + /// + /// A trio of 3D coordinates facing to the east. + /// + public static readonly Coordinates2D East = new Coordinates2D(1, 0); + + /// + /// A trio of 3D coordinates facing to the west. + /// + public static readonly Coordinates2D West = new Coordinates2D(-1, 0); + + /// + /// A trio of 3D coordinates facing to the north. + /// + public static readonly Coordinates2D North = new Coordinates2D(0, -1); + + /// + /// A trio of 3D coordinates facing to the south. + /// + public static readonly Coordinates2D South = new Coordinates2D(0, 1); + #endregion /// diff --git a/TrueCraft.Client/Rendering/BlockRenderer.cs b/TrueCraft.Client/Rendering/BlockRenderer.cs index 984cecf..5cdba07 100644 --- a/TrueCraft.Client/Rendering/BlockRenderer.cs +++ b/TrueCraft.Client/Rendering/BlockRenderer.cs @@ -51,7 +51,8 @@ namespace TrueCraft.Client.Rendering for (int _side = 0; _side < 6; _side++) { var side = (CubeFace)_side; - var quad = CreateQuad(side, offset, texture, textureIndex % texture.Length, indiciesOffset, out _indicies, color); + var quad = CreateQuad(side, offset, texture, textureIndex % texture.Length, indiciesOffset, + out _indicies, color); Array.Copy(quad, 0, verticies, _side * 4, 4); Array.Copy(_indicies, 0, indicies, _side * 6, 6); textureIndex += 4; @@ -59,8 +60,8 @@ namespace TrueCraft.Client.Rendering return verticies; } - protected static VertexPositionNormalColorTexture[] CreateQuad(CubeFace face, Vector3 offset, Vector2[] texture, int textureOffset, - int indiciesOffset, out int[] indicies, Color color) + protected static VertexPositionNormalColorTexture[] CreateQuad(CubeFace face, Vector3 offset, + Vector2[] texture, int textureOffset, int indiciesOffset, out int[] indicies, Color color) { indicies = new[] { 0, 1, 3, 1, 2, 3 }; for (int i = 0; i < indicies.Length; i++) diff --git a/TrueCraft.Client/Rendering/ChunkRenderer.cs b/TrueCraft.Client/Rendering/ChunkRenderer.cs index 34baa5a..097e5d6 100644 --- a/TrueCraft.Client/Rendering/ChunkRenderer.cs +++ b/TrueCraft.Client/Rendering/ChunkRenderer.cs @@ -39,12 +39,14 @@ namespace TrueCraft.Client.Rendering } } + private ReadOnlyWorld World { get; set; } private TrueCraftGame Game { get; set; } private IBlockRepository BlockRepository { get; set; } - public ChunkRenderer(TrueCraftGame game, IBlockRepository blockRepository) + public ChunkRenderer(ReadOnlyWorld world, TrueCraftGame game, IBlockRepository blockRepository) : base() { + World = world; BlockRepository = blockRepository; Game = game; } @@ -59,10 +61,20 @@ namespace TrueCraft.Client.Rendering Coordinates3D.West }; + private static readonly VisibleFaces[] AdjacentCoordFaces = + { + VisibleFaces.Bottom, + VisibleFaces.Top, + VisibleFaces.South, + VisibleFaces.North, + VisibleFaces.West, + VisibleFaces.East + }; + protected override bool TryRender(ReadOnlyChunk item, out Mesh result) { var state = new RenderState(); - ProcessChunk(item, state); + ProcessChunk(World, item, state); result = new ChunkMesh(item, Game, state.Verticies.ToArray(), state.OpaqueIndicies.ToArray(), state.TransparentIndicies.ToArray()); @@ -70,15 +82,125 @@ namespace TrueCraft.Client.Rendering return (result != null); } - private class RenderState + [Flags] + public enum VisibleFaces { - public readonly List Verticies = new List(); - public readonly List OpaqueIndicies = new List(); - public readonly List TransparentIndicies = new List(); - public readonly HashSet DrawableCoordinates = new HashSet(); + None = 0, + North = 1, + South = 2, + East = 4, + West = 8, + Top = 16, + Bottom = 32, + All = North | South | East | West | Top | Bottom } - private void ProcessChunk(ReadOnlyChunk chunk, RenderState state) + private class RenderState + { + public readonly List Verticies + = new List(); + public readonly List OpaqueIndicies = new List(); + public readonly List TransparentIndicies = new List(); + public readonly Dictionary DrawableCoordinates + = new Dictionary(); + } + + private void AddBottomBlock(Coordinates3D coords, RenderState state, ReadOnlyChunk chunk) + { + var desiredFaces = VisibleFaces.None; + if (coords.X == 0) + desiredFaces |= VisibleFaces.West; + else if (coords.X == Chunk.Width - 1) + desiredFaces |= VisibleFaces.East; + if (coords.Z == 0) + desiredFaces |= VisibleFaces.North; + else if (coords.Z == Chunk.Depth - 1) + desiredFaces |= VisibleFaces.South; + if (coords.Y == 0) + desiredFaces |= VisibleFaces.Bottom; + else if (coords.Y == Chunk.Depth - 1) + desiredFaces |= VisibleFaces.Top; + + VisibleFaces faces; + state.DrawableCoordinates.TryGetValue(coords, out faces); + faces |= desiredFaces; + state.DrawableCoordinates[coords] = desiredFaces; + } + + private void AddAdjacentBlocks(Coordinates3D coords, RenderState state, ReadOnlyChunk chunk) + { + // Add adjacent blocks + for (int i = 0; i < AdjacentCoordinates.Length; i++) + { + var next = coords + AdjacentCoordinates[i]; + if (next.X < 0 || next.X >= Chunk.Width + || next.Y < 0 || next.Y >= Chunk.Height + || next.Z < 0 || next.Z >= Chunk.Depth) + { + continue; + } + if (chunk.GetBlockId(next) != 0) + { + VisibleFaces faces; + state.DrawableCoordinates.TryGetValue(next, out faces); + faces |= AdjacentCoordFaces[i]; + state.DrawableCoordinates[next] = faces; + } + } + } + + private void UpdateFacesFromAdjacent(Coordinates3D adjacent, ReadOnlyChunk chunk, + VisibleFaces mod, ref VisibleFaces faces) + { + if (chunk == null) + return; + var provider = BlockRepository.GetBlockProvider(chunk.GetBlockId(adjacent)); + if (!provider.Opaque) + faces |= mod; + } + + private void AddChunkBoundaryBlocks(Coordinates3D coords, RenderState state, ReadOnlyChunk chunk) + { + VisibleFaces faces; + if (!state.DrawableCoordinates.TryGetValue(coords, out faces)) + faces = VisibleFaces.None; + VisibleFaces oldFaces = faces; + + if (coords.X == 0) + { + var adjacent = coords; + adjacent.X = Chunk.Width - 1; + var nextChunk = World.GetChunk(chunk.Chunk.Coordinates + Coordinates2D.West); + UpdateFacesFromAdjacent(adjacent, nextChunk, VisibleFaces.West, ref faces); + } + else if (coords.X == Chunk.Width - 1) + { + var adjacent = coords; + adjacent.X = 0; + var nextChunk = World.GetChunk(chunk.Chunk.Coordinates + Coordinates2D.East); + UpdateFacesFromAdjacent(adjacent, nextChunk, VisibleFaces.East, ref faces); + } + + if (coords.Z == 0) + { + var adjacent = coords; + adjacent.Z = Chunk.Depth - 1; + var nextChunk = World.GetChunk(chunk.Chunk.Coordinates + Coordinates2D.North); + UpdateFacesFromAdjacent(adjacent, nextChunk, VisibleFaces.North, ref faces); + } + else if (coords.Z == Chunk.Depth - 1) + { + var adjacent = coords; + adjacent.Z = 0; + var nextChunk = World.GetChunk(chunk.Chunk.Coordinates + Coordinates2D.South); + UpdateFacesFromAdjacent(adjacent, nextChunk, VisibleFaces.South, ref faces); + } + + if (oldFaces != faces) + state.DrawableCoordinates[coords] = faces; + } + + private void ProcessChunk(ReadOnlyWorld world, ReadOnlyChunk chunk, RenderState state) { state.Verticies.Clear(); state.OpaqueIndicies.Clear(); @@ -94,26 +216,16 @@ namespace TrueCraft.Client.Rendering var coords = new Coordinates3D(x, y, z); var id = chunk.GetBlockId(coords); var provider = BlockRepository.GetBlockProvider(id); - if (id != 0 && (coords.X == 0 || coords.X == Chunk.Width - 1 - || coords.Y == 0 || coords.Y == Chunk.Height - 1 - || coords.Z == 0 || coords.Z == Chunk.Depth - 1)) - { - state.DrawableCoordinates.Add(coords); - } + if (id != 0 && coords.Y == 0) + AddBottomBlock(coords, state, chunk); if (!provider.Opaque) + AddAdjacentBlocks(coords, state, chunk); + else { - // Add adjacent blocks - foreach (var a in AdjacentCoordinates) + if (coords.X == 0 || coords.X == Chunk.Width - 1 || + coords.Z == 0 || coords.Z == Chunk.Depth - 1) { - var next = coords + a; - if (next.X < 0 || next.X >= Chunk.Width - || next.Y < 0 || next.Y >= Chunk.Height - || next.Z < 0 || next.Z >= Chunk.Depth) - { - continue; - } - if (chunk.GetBlockId(next) != 0) - state.DrawableCoordinates.Add(next); + AddChunkBoundaryBlocks(coords, state, chunk); } } } @@ -124,13 +236,14 @@ namespace TrueCraft.Client.Rendering { var coords = enumerator.Current; enumerator.MoveNext(); + var c = coords.Key; var descriptor = new BlockDescriptor { - ID = chunk.GetBlockId(coords), - Metadata = chunk.GetMetadata(coords), - BlockLight = chunk.GetBlockLight(coords), - SkyLight = chunk.GetSkyLight(coords), - Coordinates = coords, + ID = chunk.GetBlockId(coords.Key), + Metadata = chunk.GetMetadata(coords.Key), + BlockLight = chunk.GetBlockLight(coords.Key), + SkyLight = chunk.GetSkyLight(coords.Key), + Coordinates = coords.Key, Chunk = chunk.Chunk }; var provider = BlockRepository.GetBlockProvider(descriptor.ID); @@ -138,7 +251,7 @@ namespace TrueCraft.Client.Rendering { int[] i; var v = BlockRenderer.RenderBlock(provider, descriptor, - new Vector3(chunk.X * Chunk.Width + coords.X, coords.Y, chunk.Z * Chunk.Depth + coords.Z), + new Vector3(chunk.X * Chunk.Width + c.X, c.Y, chunk.Z * Chunk.Depth + c.Z), state.Verticies.Count, out i); state.Verticies.AddRange(v); state.OpaqueIndicies.AddRange(i); @@ -147,7 +260,7 @@ namespace TrueCraft.Client.Rendering { int[] i; var v = BlockRenderer.RenderBlock(provider, descriptor, - new Vector3(chunk.X * Chunk.Width + coords.X, coords.Y, chunk.Z * Chunk.Depth + coords.Z), + new Vector3(chunk.X * Chunk.Width + c.X, c.Y, chunk.Z * Chunk.Depth + c.Z), state.Verticies.Count, out i); state.Verticies.AddRange(v); state.TransparentIndicies.AddRange(i); diff --git a/TrueCraft.Client/TrueCraft.Client.csproj b/TrueCraft.Client/TrueCraft.Client.csproj index e41111a..4a5fe6b 100644 --- a/TrueCraft.Client/TrueCraft.Client.csproj +++ b/TrueCraft.Client/TrueCraft.Client.csproj @@ -47,18 +47,15 @@ localhost TestUser - v4.5 + true - v4.5 - v4.5 - v4.5 @@ -68,6 +65,9 @@ DEBUG; true + + false + ..\lib\Ionic.Zip.Reduced.dll @@ -190,7 +190,7 @@ PreserveNewest - PreserveNewest + Always PreserveNewest diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index 4c34dda..9e1904d 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -78,7 +78,7 @@ namespace TrueCraft.Client Interfaces = new List(); SpriteBatch = new SpriteBatch(GraphicsDevice); base.Initialize(); // (calls LoadContent) - ChunkConverter = new ChunkRenderer(this, Client.World.World.BlockRepository); + ChunkConverter = new ChunkRenderer(Client.World, this, Client.World.World.BlockRepository); Client.ChunkLoaded += (sender, e) => ChunkConverter.Enqueue(e.Chunk); //Client.ChunkModified += (sender, e) => ChunkConverter.Enqueue(e.Chunk, true); ChunkConverter.MeshCompleted += ChunkConverter_MeshGenerated; @@ -302,7 +302,7 @@ namespace TrueCraft.Client Mouse.SetPosition(centerX, centerY); var look = new Vector2((centerX - e.X), (centerY - e.Y)) - * (float)(GameTime.ElapsedGameTime.TotalSeconds * 70); + * (float)(GameTime.ElapsedGameTime.TotalSeconds * 90); Client.Yaw += look.X; Client.Pitch += look.Y;