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>
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
/// <summary>

View File

@ -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++)

View File

@ -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<VertexPositionNormalColorTexture> Verticies = new List<VertexPositionNormalColorTexture>();
public readonly List<int> OpaqueIndicies = new List<int>();
public readonly List<int> TransparentIndicies = new List<int>();
public readonly HashSet<Coordinates3D> DrawableCoordinates = new HashSet<Coordinates3D>();
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<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.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);

View File

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

View File

@ -78,7 +78,7 @@ namespace TrueCraft.Client
Interfaces = new List<IGameInterface>();
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;