Avoid rendering unseen blocks at chunk boundaries

This commit is contained in:
Drew DeVault 2015-09-20 15:49:31 -04:00
parent 94b8e2e355
commit 2d0ce96cc0
5 changed files with 176 additions and 42 deletions

View File

@ -250,6 +250,26 @@ namespace TrueCraft.API
/// </summary> /// </summary>
public static readonly Coordinates2D Right = new Coordinates2D(1, 0); public static readonly Coordinates2D Right = new Coordinates2D(1, 0);
/// <summary>
/// A trio of 3D coordinates facing to the east.
/// </summary>
public static readonly Coordinates2D East = new Coordinates2D(1, 0);
/// <summary>
/// A trio of 3D coordinates facing to the west.
/// </summary>
public static readonly Coordinates2D West = new Coordinates2D(-1, 0);
/// <summary>
/// A trio of 3D coordinates facing to the north.
/// </summary>
public static readonly Coordinates2D North = new Coordinates2D(0, -1);
/// <summary>
/// A trio of 3D coordinates facing to the south.
/// </summary>
public static readonly Coordinates2D South = new Coordinates2D(0, 1);
#endregion #endregion
/// <summary> /// <summary>

View File

@ -51,7 +51,8 @@ namespace TrueCraft.Client.Rendering
for (int _side = 0; _side < 6; _side++) for (int _side = 0; _side < 6; _side++)
{ {
var side = (CubeFace)_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(quad, 0, verticies, _side * 4, 4);
Array.Copy(_indicies, 0, indicies, _side * 6, 6); Array.Copy(_indicies, 0, indicies, _side * 6, 6);
textureIndex += 4; textureIndex += 4;
@ -59,8 +60,8 @@ namespace TrueCraft.Client.Rendering
return verticies; return verticies;
} }
protected static VertexPositionNormalColorTexture[] CreateQuad(CubeFace face, Vector3 offset, Vector2[] texture, int textureOffset, protected static VertexPositionNormalColorTexture[] CreateQuad(CubeFace face, Vector3 offset,
int indiciesOffset, out int[] indicies, Color color) Vector2[] texture, int textureOffset, int indiciesOffset, out int[] indicies, Color color)
{ {
indicies = new[] { 0, 1, 3, 1, 2, 3 }; indicies = new[] { 0, 1, 3, 1, 2, 3 };
for (int i = 0; i < indicies.Length; i++) for (int i = 0; i < indicies.Length; i++)

View File

@ -39,12 +39,14 @@ namespace TrueCraft.Client.Rendering
} }
} }
private ReadOnlyWorld World { get; set; }
private TrueCraftGame Game { get; set; } private TrueCraftGame Game { get; set; }
private IBlockRepository BlockRepository { get; set; } private IBlockRepository BlockRepository { get; set; }
public ChunkRenderer(TrueCraftGame game, IBlockRepository blockRepository) public ChunkRenderer(ReadOnlyWorld world, TrueCraftGame game, IBlockRepository blockRepository)
: base() : base()
{ {
World = world;
BlockRepository = blockRepository; BlockRepository = blockRepository;
Game = game; Game = game;
} }
@ -59,10 +61,20 @@ namespace TrueCraft.Client.Rendering
Coordinates3D.West 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) protected override bool TryRender(ReadOnlyChunk item, out Mesh result)
{ {
var state = new RenderState(); var state = new RenderState();
ProcessChunk(item, state); ProcessChunk(World, item, state);
result = new ChunkMesh(item, Game, state.Verticies.ToArray(), result = new ChunkMesh(item, Game, state.Verticies.ToArray(),
state.OpaqueIndicies.ToArray(), state.TransparentIndicies.ToArray()); state.OpaqueIndicies.ToArray(), state.TransparentIndicies.ToArray());
@ -70,15 +82,125 @@ namespace TrueCraft.Client.Rendering
return (result != null); return (result != null);
} }
private class RenderState [Flags]
public enum VisibleFaces
{ {
public readonly List<VertexPositionNormalColorTexture> Verticies = new List<VertexPositionNormalColorTexture>(); None = 0,
public readonly List<int> OpaqueIndicies = new List<int>(); North = 1,
public readonly List<int> TransparentIndicies = new List<int>(); South = 2,
public readonly HashSet<Coordinates3D> DrawableCoordinates = new HashSet<Coordinates3D>(); 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<VertexPositionNormalColorTexture> Verticies
= new List<VertexPositionNormalColorTexture>();
public readonly List<int> OpaqueIndicies = new List<int>();
public readonly List<int> TransparentIndicies = new List<int>();
public readonly Dictionary<Coordinates3D, VisibleFaces> DrawableCoordinates
= new Dictionary<Coordinates3D, VisibleFaces>();
}
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.Verticies.Clear();
state.OpaqueIndicies.Clear(); state.OpaqueIndicies.Clear();
@ -94,26 +216,16 @@ namespace TrueCraft.Client.Rendering
var coords = new Coordinates3D(x, y, z); var coords = new Coordinates3D(x, y, z);
var id = chunk.GetBlockId(coords); var id = chunk.GetBlockId(coords);
var provider = BlockRepository.GetBlockProvider(id); var provider = BlockRepository.GetBlockProvider(id);
if (id != 0 && (coords.X == 0 || coords.X == Chunk.Width - 1 if (id != 0 && coords.Y == 0)
|| coords.Y == 0 || coords.Y == Chunk.Height - 1 AddBottomBlock(coords, state, chunk);
|| coords.Z == 0 || coords.Z == Chunk.Depth - 1))
{
state.DrawableCoordinates.Add(coords);
}
if (!provider.Opaque) if (!provider.Opaque)
AddAdjacentBlocks(coords, state, chunk);
else
{ {
// Add adjacent blocks if (coords.X == 0 || coords.X == Chunk.Width - 1 ||
foreach (var a in AdjacentCoordinates) coords.Z == 0 || coords.Z == Chunk.Depth - 1)
{ {
var next = coords + a; AddChunkBoundaryBlocks(coords, state, chunk);
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);
} }
} }
} }
@ -124,13 +236,14 @@ namespace TrueCraft.Client.Rendering
{ {
var coords = enumerator.Current; var coords = enumerator.Current;
enumerator.MoveNext(); enumerator.MoveNext();
var c = coords.Key;
var descriptor = new BlockDescriptor var descriptor = new BlockDescriptor
{ {
ID = chunk.GetBlockId(coords), ID = chunk.GetBlockId(coords.Key),
Metadata = chunk.GetMetadata(coords), Metadata = chunk.GetMetadata(coords.Key),
BlockLight = chunk.GetBlockLight(coords), BlockLight = chunk.GetBlockLight(coords.Key),
SkyLight = chunk.GetSkyLight(coords), SkyLight = chunk.GetSkyLight(coords.Key),
Coordinates = coords, Coordinates = coords.Key,
Chunk = chunk.Chunk Chunk = chunk.Chunk
}; };
var provider = BlockRepository.GetBlockProvider(descriptor.ID); var provider = BlockRepository.GetBlockProvider(descriptor.ID);
@ -138,7 +251,7 @@ namespace TrueCraft.Client.Rendering
{ {
int[] i; int[] i;
var v = BlockRenderer.RenderBlock(provider, descriptor, 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.Count, out i);
state.Verticies.AddRange(v); state.Verticies.AddRange(v);
state.OpaqueIndicies.AddRange(i); state.OpaqueIndicies.AddRange(i);
@ -147,7 +260,7 @@ namespace TrueCraft.Client.Rendering
{ {
int[] i; int[] i;
var v = BlockRenderer.RenderBlock(provider, descriptor, 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.Count, out i);
state.Verticies.AddRange(v); state.Verticies.AddRange(v);
state.TransparentIndicies.AddRange(i); state.TransparentIndicies.AddRange(i);

View File

@ -47,18 +47,15 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<Commandlineparameters>localhost TestUser</Commandlineparameters> <Commandlineparameters>localhost TestUser</Commandlineparameters>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<DebugType> <DebugType>
</DebugType> </DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Optimized Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Optimized Debug|AnyCPU' ">
<DebugType> <DebugType>
@ -68,6 +65,9 @@
<DefineConstants>DEBUG;</DefineConstants> <DefineConstants>DEBUG;</DefineConstants>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Optimized Debug|x86' ">
<Optimize>false</Optimize>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Ionic.Zip.Reduced"> <Reference Include="Ionic.Zip.Reduced">
<HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath> <HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath>
@ -190,7 +190,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Content\terrain.png"> <Content Include="Content\terrain.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<None Include="Content\Fonts\Pixel_Bold.fnt"> <None Include="Content\Fonts\Pixel_Bold.fnt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@ -78,7 +78,7 @@ namespace TrueCraft.Client
Interfaces = new List<IGameInterface>(); Interfaces = new List<IGameInterface>();
SpriteBatch = new SpriteBatch(GraphicsDevice); SpriteBatch = new SpriteBatch(GraphicsDevice);
base.Initialize(); // (calls LoadContent) 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.ChunkLoaded += (sender, e) => ChunkConverter.Enqueue(e.Chunk);
//Client.ChunkModified += (sender, e) => ChunkConverter.Enqueue(e.Chunk, true); //Client.ChunkModified += (sender, e) => ChunkConverter.Enqueue(e.Chunk, true);
ChunkConverter.MeshCompleted += ChunkConverter_MeshGenerated; ChunkConverter.MeshCompleted += ChunkConverter_MeshGenerated;
@ -302,7 +302,7 @@ namespace TrueCraft.Client
Mouse.SetPosition(centerX, centerY); Mouse.SetPosition(centerX, centerY);
var look = new Vector2((centerX - e.X), (centerY - e.Y)) 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.Yaw += look.X;
Client.Pitch += look.Y; Client.Pitch += look.Y;