From 341093384deef6336816ae5840d34ca25d8a4ea4 Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Wed, 17 Jun 2015 19:23:24 -0400 Subject: [PATCH 1/5] Initial work on improving rendering; separated threading from mesh generation --- TrueCraft.Client/Rendering/ChunkMesh.cs | 17 ++ TrueCraft.Client/Rendering/ChunkRenderer.cs | 106 ++-------- TrueCraft.Client/Rendering/Renderer.cs | 193 ++++++++++++++++++ .../Rendering/RendererEventArgs.cs | 39 ++++ TrueCraft.Client/TrueCraft.Client.csproj | 2 + TrueCraft.Client/TrueCraftGame.cs | 47 ++--- TrueCraft.sln | 93 +++++---- 7 files changed, 338 insertions(+), 159 deletions(-) create mode 100644 TrueCraft.Client/Rendering/Renderer.cs create mode 100644 TrueCraft.Client/Rendering/RendererEventArgs.cs diff --git a/TrueCraft.Client/Rendering/ChunkMesh.cs b/TrueCraft.Client/Rendering/ChunkMesh.cs index 26b934b..3ed766a 100644 --- a/TrueCraft.Client/Rendering/ChunkMesh.cs +++ b/TrueCraft.Client/Rendering/ChunkMesh.cs @@ -29,6 +29,23 @@ namespace TrueCraft.Client.Rendering Vertices = vertices; SetSubmesh(0, indices); } + + /// + /// + /// + /// + /// + /// + /// + /// + public ChunkMesh(ReadOnlyChunk chunk, GraphicsDevice device, VertexPositionNormalTexture[] vertices, int[] opaqueIndices, int[] transparentIndices) + : base(device, 2, true) + { + Chunk = chunk; + Vertices = vertices; + SetSubmesh(0, opaqueIndices); + SetSubmesh(1, transparentIndices); + } /// /// diff --git a/TrueCraft.Client/Rendering/ChunkRenderer.cs b/TrueCraft.Client/Rendering/ChunkRenderer.cs index accd367..69e1259 100644 --- a/TrueCraft.Client/Rendering/ChunkRenderer.cs +++ b/TrueCraft.Client/Rendering/ChunkRenderer.cs @@ -19,7 +19,7 @@ namespace TrueCraft.Client.Rendering /// A daemon of sorts that creates meshes from chunks. /// Passing meshes back is NOT thread-safe. /// - public class ChunkRenderer + public class ChunkRenderer : Renderer { public class ChunkSorter : Comparer { @@ -39,85 +39,16 @@ namespace TrueCraft.Client.Rendering } } - public class MeshGeneratedEventArgs : EventArgs - { - public bool Transparent { get; set; } - public ChunkMesh Mesh { get; set; } - - public MeshGeneratedEventArgs(ChunkMesh mesh, bool transparent) - { - Transparent = transparent; - Mesh = mesh; - } - } - - public event EventHandler MeshGenerated; - - private ConcurrentQueue ChunkQueue { get; set; } private GraphicsDevice GraphicsDevice { get; set; } private IBlockRepository BlockRepository { get; set; } - private List> RenderThreads { get; set; } public ChunkRenderer(GraphicsDevice graphics, IBlockRepository blockRepository) + : base() { - ChunkQueue = new ConcurrentQueue(); - RenderThreads = new List>(); - var threads = Environment.ProcessorCount - 2; - if (threads < 1) - threads = 1; - for (int i = 0; i < threads; i++) - { - var token = new CancellationTokenSource(); - var worker = new Thread(() => DoChunks(token)); - worker.IsBackground = true; - RenderThreads.Add(new Tuple(worker, token)); - } - BlockRepository = blockRepository; GraphicsDevice = graphics; } - public void QueueChunk(ReadOnlyChunk chunk) - { - ChunkQueue.Enqueue(chunk); - } - - public void QueueHighPriorityChunk(ReadOnlyChunk chunk) - { - // TODO: Overwrite existing chunks - Task.Factory.StartNew(() => - { - ProcessChunk(chunk, new RenderState()); - }); - } - - public void Start() - { - RenderThreads.ForEach(t => t.Item1.Start()); - } - - public void Stop() - { - RenderThreads.ForEach(t => t.Item2.Cancel()); - } - - private void DoChunks(CancellationTokenSource token) - { - var state = new RenderState(); - while (true) - { - if (token.Token.IsCancellationRequested) - return; - - ReadOnlyChunk chunk; - if (ChunkQueue.TryDequeue(out chunk)) - { - ProcessChunk(chunk, state); - } - Thread.Sleep(1); - } - } - private static readonly Coordinates3D[] AdjacentCoordinates = { Coordinates3D.Up, @@ -128,20 +59,30 @@ namespace TrueCraft.Client.Rendering Coordinates3D.West }; + protected override bool TryRender(ReadOnlyChunk item, out Mesh result) + { + var state = new RenderState(); + ProcessChunk(item, state); + + result = new ChunkMesh(item, GraphicsDevice, state.Verticies.ToArray(), + state.OpaqueIndicies.ToArray(), state.TransparentIndicies.ToArray()); + + return (result != null); + + } + private class RenderState { - public readonly List OpaqueVerticies = new List(); + public readonly List Verticies = new List(); public readonly List OpaqueIndicies = new List(); - public readonly List TransparentVerticies = new List(); public readonly List TransparentIndicies = new List(); public readonly HashSet DrawableCoordinates = new HashSet(); } private void ProcessChunk(ReadOnlyChunk chunk, RenderState state) { - state.OpaqueVerticies.Clear(); + state.Verticies.Clear(); state.OpaqueIndicies.Clear(); - state.TransparentVerticies.Clear(); state.TransparentIndicies.Clear(); state.DrawableCoordinates.Clear(); @@ -198,8 +139,8 @@ 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), - state.OpaqueVerticies.Count, out i); - state.OpaqueVerticies.AddRange(v); + state.Verticies.Count, out i); + state.Verticies.AddRange(v); state.OpaqueIndicies.AddRange(i); } else @@ -207,18 +148,11 @@ 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), - state.TransparentVerticies.Count, out i); - state.TransparentVerticies.AddRange(v); + state.Verticies.Count, out i); + state.Verticies.AddRange(v); state.TransparentIndicies.AddRange(i); } } - var opaque = new ChunkMesh(chunk, GraphicsDevice, state.OpaqueVerticies.ToArray(), state.OpaqueIndicies.ToArray()); - var transparent = new ChunkMesh(chunk, GraphicsDevice, state.TransparentVerticies.ToArray(), state.TransparentIndicies.ToArray()); - if (MeshGenerated != null) - { - MeshGenerated(this, new MeshGeneratedEventArgs(opaque, false)); - MeshGenerated(this, new MeshGeneratedEventArgs(transparent, true)); - } } } } diff --git a/TrueCraft.Client/Rendering/Renderer.cs b/TrueCraft.Client/Rendering/Renderer.cs new file mode 100644 index 0000000..8591742 --- /dev/null +++ b/TrueCraft.Client/Rendering/Renderer.cs @@ -0,0 +1,193 @@ +using System; +using System.Threading; +using System.Collections.Concurrent; + +namespace TrueCraft.Client.Rendering +{ + /// + /// Abstract base class for renderers of meshes. + /// + /// The object to render into a mesh. + public abstract class Renderer : IDisposable + { + private readonly object _syncLock = + new object(); + + /// + /// + /// + public event EventHandler> MeshCompleted; + + private volatile bool _isRunning; + private Thread _rendererThread; + private ConcurrentQueue _items, _priorityItems; + private volatile bool _isDisposed; + + /// + /// Gets whether this renderer is running. + /// + public bool IsRunning + { + get + { + if (_isDisposed) + throw new ObjectDisposedException(GetType().Name); + return _isRunning; + } + } + + /// + /// Gets whether this renderer is disposed of. + /// + public bool IsDisposed + { + get { return _isDisposed; } + } + + /// + /// + /// + protected Renderer() + { + lock (_syncLock) + { + _isRunning = false; + _rendererThread = new Thread(DoRendering); + _items = new ConcurrentQueue(); _priorityItems = new ConcurrentQueue(); + _isDisposed = false; + } + } + + /// + /// Starts this renderer. + /// + public void Start() + { + if (_isDisposed) + throw new ObjectDisposedException(GetType().Name); + + if (_isRunning) return; + lock (_syncLock) + { + _isRunning = true; + _rendererThread.Start(null); + } + } + + /// + /// + /// + /// + private void DoRendering(object obj) + { + while (_isRunning) + { + var item = default(T); + var result = default(Mesh); + + lock (_syncLock) + { + if (_priorityItems.TryDequeue(out item) && TryRender(item, out result)) + { + var args = new RendererEventArgs(item, result, true); + if (MeshCompleted != null) + MeshCompleted(this, args); + } + else if (_items.TryDequeue(out item) && TryRender(item, out result)) + { + var args = new RendererEventArgs(item, result, false); + if (MeshCompleted != null) + MeshCompleted(this, args); + } + } + + if (item == null) // We don't have any work, so sleep for a bit. + Thread.Sleep(100); + } + } + + /// + /// + /// + /// + /// + /// + protected abstract bool TryRender(T item, out Mesh result); + + /// + /// Stops this renderer. + /// + public void Stop() + { + if (_isDisposed) + throw new ObjectDisposedException(GetType().Name); + + if (!_isRunning) return; + lock (_syncLock) + { + _isRunning = false; + _rendererThread.Join(); + } + } + + /// + /// Enqueues an item to this renderer for rendering. + /// + /// + /// + public void Enqueue(T item, bool hasPriority = false) + { + if (_isDisposed) + throw new ObjectDisposedException(GetType().Name); + + if (!_isRunning) return; + lock (_syncLock) + { + if (hasPriority) + _priorityItems.Enqueue(item); + else + _items.Enqueue(item); + } + } + + /// + /// Disposes of this renderer. + /// + public void Dispose() + { + if (_isDisposed) + return; + + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes of this renderer. + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Stop(); + lock (_syncLock) + { + _rendererThread = null; + _items = null; _priorityItems = null; + _isDisposed = true; + } + } + else + throw new NotSupportedException(); // We should 'encourage' developers to dispose of renderers properly. + } + + /// + /// Finalizes this renderer. + /// + ~Renderer() + { + Dispose(false); + } + } +} diff --git a/TrueCraft.Client/Rendering/RendererEventArgs.cs b/TrueCraft.Client/Rendering/RendererEventArgs.cs new file mode 100644 index 0000000..f14ff60 --- /dev/null +++ b/TrueCraft.Client/Rendering/RendererEventArgs.cs @@ -0,0 +1,39 @@ +using System; + +namespace TrueCraft.Client.Rendering +{ + /// + /// + /// + /// + public sealed class RendererEventArgs : EventArgs + { + /// + /// + /// + public T Item { get; private set; } + + /// + /// + /// + public Mesh Result { get; private set; } + + /// + /// + /// + public bool IsPriority { get; private set; } + + /// + /// + /// + /// + /// + /// + public RendererEventArgs(T item, Mesh result, bool isPriority) + { + Item = item; + Result = result; + IsPriority = isPriority; + } + } +} diff --git a/TrueCraft.Client/TrueCraft.Client.csproj b/TrueCraft.Client/TrueCraft.Client.csproj index 0be89e5..44d881a 100644 --- a/TrueCraft.Client/TrueCraft.Client.csproj +++ b/TrueCraft.Client/TrueCraft.Client.csproj @@ -105,6 +105,8 @@ + + diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index b747325..cb7fbb6 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -28,11 +28,9 @@ namespace TrueCraft.Client private IPEndPoint EndPoint { get; set; } private ChunkRenderer ChunkConverter { get; set; } private DateTime NextPhysicsUpdate { get; set; } - private List ChunkMeshes { get; set; } + private List ChunkMeshes { get; set; } private ConcurrentBag PendingMainThreadActions { get; set; } - private ConcurrentBag IncomingChunks { get; set; } - private ConcurrentBag IncomingTransparentChunks { get; set; } - private List TransparentChunkMeshes { get; set; } + private ConcurrentBag IncomingChunks { get; set; } public ChatInterface ChatInterface { get; set; } private RenderTarget2D RenderTarget; private BoundingFrustum CameraView; @@ -51,16 +49,15 @@ namespace TrueCraft.Client Window.Title = "TrueCraft"; Content.RootDirectory = "Content"; Graphics = new GraphicsDeviceManager(this); + Graphics.SynchronizeWithVerticalRetrace = false; Graphics.IsFullScreen = UserSettings.Local.IsFullscreen; Graphics.PreferredBackBufferWidth = UserSettings.Local.WindowResolution.Width; Graphics.PreferredBackBufferHeight = UserSettings.Local.WindowResolution.Height; Client = client; EndPoint = endPoint; NextPhysicsUpdate = DateTime.MinValue; - ChunkMeshes = new List(); - TransparentChunkMeshes = new List(); - IncomingChunks = new ConcurrentBag(); - IncomingTransparentChunks = new ConcurrentBag(); + ChunkMeshes = new List(); + IncomingChunks = new ConcurrentBag(); PendingMainThreadActions = new ConcurrentBag(); MouseCaptured = true; @@ -79,9 +76,9 @@ namespace TrueCraft.Client SpriteBatch = new SpriteBatch(GraphicsDevice); base.Initialize(); // (calls LoadContent) ChunkConverter = new ChunkRenderer(Graphics.GraphicsDevice, Client.World.World.BlockRepository); - Client.ChunkLoaded += (sender, e) => ChunkConverter.QueueChunk(e.Chunk); - Client.ChunkModified += (sender, e) => ChunkConverter.QueueHighPriorityChunk(e.Chunk); - ChunkConverter.MeshGenerated += ChunkConverter_MeshGenerated; + Client.ChunkLoaded += (sender, e) => ChunkConverter.Enqueue(e.Chunk); + Client.ChunkModified += (sender, e) => ChunkConverter.Enqueue(e.Chunk, true); + ChunkConverter.MeshCompleted += ChunkConverter_MeshGenerated; ChunkConverter.Start(); Client.PropertyChanged += HandleClientPropertyChanged; Client.Connect(EndPoint); @@ -103,12 +100,9 @@ namespace TrueCraft.Client false, GraphicsDevice.PresentationParameters.BackBufferFormat, DepthFormat.Depth24); } - void ChunkConverter_MeshGenerated(object sender, ChunkRenderer.MeshGeneratedEventArgs e) + void ChunkConverter_MeshGenerated(object sender, RendererEventArgs e) { - if (e.Transparent) - IncomingTransparentChunks.Add(e.Mesh); - else - IncomingChunks.Add(e.Mesh); + IncomingChunks.Add(e.Result); } void HandleClientPropertyChanged(object sender, PropertyChangedEventArgs e) @@ -119,7 +113,7 @@ namespace TrueCraft.Client UpdateCamera(); var sorter = new ChunkRenderer.ChunkSorter(new Coordinates3D( (int)Client.Position.X, 0, (int)Client.Position.Z)); - PendingMainThreadActions.Add(() => TransparentChunkMeshes.Sort(sorter)); + PendingMainThreadActions.Add(() => ChunkMeshes.Sort(sorter)); break; } } @@ -287,16 +281,11 @@ namespace TrueCraft.Client i.Update(gameTime); } - ChunkMesh mesh; + Mesh mesh; if (IncomingChunks.TryTake(out mesh)) { - var existing = ChunkMeshes.SingleOrDefault(m => m.Chunk.Chunk.Coordinates == mesh.Chunk.Chunk.Coordinates); - if (existing != null) - ChunkMeshes.Remove(existing); ChunkMeshes.Add(mesh); } - if (IncomingTransparentChunks.TryTake(out mesh)) // TODO: re-render transparent meshes - TransparentChunkMeshes.Add(mesh); Action action; if (PendingMainThreadActions.TryTake(out action)) action(); @@ -364,17 +353,17 @@ namespace TrueCraft.Client { verticies += ChunkMeshes[i].GetTotalVertices(); chunks++; - ChunkMeshes[i].Draw(OpaqueEffect); + ChunkMeshes[i].Draw(OpaqueEffect, 0); } } GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; - for (int i = 0; i < TransparentChunkMeshes.Count; i++) + for (int i = 0; i < ChunkMeshes.Count; i++) { - if (CameraView.Intersects(TransparentChunkMeshes[i].BoundingBox)) + if (CameraView.Intersects(ChunkMeshes[i].BoundingBox)) { - if (!TransparentChunkMeshes[i].IsDisposed) - verticies += TransparentChunkMeshes[i].GetTotalVertices(); - TransparentChunkMeshes[i].Draw(TransparentEffect); + if (!ChunkMeshes[i].IsDisposed) + verticies += ChunkMeshes[i].GetTotalVertices(); + ChunkMeshes[i].Draw(TransparentEffect, 1); } } GraphicsDevice.DepthStencilState = DepthStencilState.Default; diff --git a/TrueCraft.sln b/TrueCraft.sln index 5449501..c09f6f1 100644 --- a/TrueCraft.sln +++ b/TrueCraft.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCraft", "TrueCraft\TrueCraft.csproj", "{C1C47EF5-2D8A-4231-AAA8-F651F52F480E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCraft.API", "TrueCraft.API\TrueCraft.API.csproj", "{FEE55B54-91B0-4325-A2C3-D576C0B7A81F}" @@ -19,44 +21,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCraft.Core.Test", "True EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x64.ActiveCfg = Debug|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x64.Build.0 = Debug|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x86.ActiveCfg = Debug|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x86.Build.0 = Debug|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x64.ActiveCfg = Release|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x64.Build.0 = Release|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x86.ActiveCfg = Release|Any CPU - {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x86.Build.0 = Release|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x64.ActiveCfg = Debug|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x64.Build.0 = Debug|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x86.ActiveCfg = Debug|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x86.Build.0 = Debug|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x64.ActiveCfg = Release|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x64.Build.0 = Release|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x86.ActiveCfg = Release|Any CPU - {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x86.Build.0 = Release|Any CPU - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x64.ActiveCfg = Debug|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x64.Build.0 = Debug|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x86.ActiveCfg = Debug|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x86.Build.0 = Debug|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x64.ActiveCfg = Release|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x64.Build.0 = Release|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x86.ActiveCfg = Release|x86 - {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x86.Build.0 = Release|x86 - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x64.ActiveCfg = Debug|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x64.Build.0 = Debug|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x86.ActiveCfg = Debug|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x86.Build.0 = Debug|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x64.ActiveCfg = Release|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x64.Build.0 = Release|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x86.ActiveCfg = Release|Any CPU - {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x86.Build.0 = Release|Any CPU {C1C47EF5-2D8A-4231-AAA8-F651F52F480E}.Debug|x64.ActiveCfg = Debug|x86 {C1C47EF5-2D8A-4231-AAA8-F651F52F480E}.Debug|x64.Build.0 = Debug|x86 {C1C47EF5-2D8A-4231-AAA8-F651F52F480E}.Debug|x86.ActiveCfg = Debug|x86 @@ -65,14 +35,6 @@ Global {C1C47EF5-2D8A-4231-AAA8-F651F52F480E}.Release|x64.Build.0 = Release|x86 {C1C47EF5-2D8A-4231-AAA8-F651F52F480E}.Release|x86.ActiveCfg = Release|x86 {C1C47EF5-2D8A-4231-AAA8-F651F52F480E}.Release|x86.Build.0 = Release|x86 - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x64.ActiveCfg = Debug|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x64.Build.0 = Debug|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x86.ActiveCfg = Debug|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x86.Build.0 = Debug|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x64.ActiveCfg = Release|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x64.Build.0 = Release|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x86.ActiveCfg = Release|Any CPU - {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x86.Build.0 = Release|Any CPU {FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Debug|x64.ActiveCfg = Debug|Any CPU {FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Debug|x64.Build.0 = Debug|Any CPU {FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -81,6 +43,49 @@ Global {FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Release|x64.Build.0 = Release|Any CPU {FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Release|x86.ActiveCfg = Release|Any CPU {FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Release|x86.Build.0 = Release|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x64.Build.0 = Debug|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Debug|x86.Build.0 = Debug|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x64.ActiveCfg = Release|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x64.Build.0 = Release|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x86.ActiveCfg = Release|Any CPU + {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}.Release|x86.Build.0 = Release|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x64.ActiveCfg = Debug|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x64.Build.0 = Debug|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x86.ActiveCfg = Debug|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Debug|x86.Build.0 = Debug|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x64.ActiveCfg = Release|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x64.Build.0 = Release|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x86.ActiveCfg = Release|Any CPU + {4488498D-976D-4DA3-BF72-109531AF0488}.Release|x86.Build.0 = Release|Any CPU + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x64.ActiveCfg = Debug|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x64.Build.0 = Debug|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x86.ActiveCfg = Debug|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Debug|x86.Build.0 = Debug|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x64.ActiveCfg = Release|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x64.Build.0 = Release|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x86.ActiveCfg = Release|x86 + {A6516869-A2FB-4E31-85C8-2285490CB32C}.Release|x86.Build.0 = Release|x86 + {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x64.ActiveCfg = Debug|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x64.Build.0 = Debug|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x86.ActiveCfg = Debug|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Debug|x86.Build.0 = Debug|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x64.ActiveCfg = Release|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x64.Build.0 = Release|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x86.ActiveCfg = Release|Any CPU + {6604F17A-552E-405D-B327-37C8B1648C86}.Release|x86.Build.0 = Release|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x64.ActiveCfg = Debug|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x64.Build.0 = Debug|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x86.ActiveCfg = Debug|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Debug|x86.Build.0 = Debug|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x64.ActiveCfg = Release|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x64.Build.0 = Release|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x86.ActiveCfg = Release|Any CPU + {BCFDCD93-C23E-49E6-9767-A887B3C2A709}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {BCFDCD93-C23E-49E6-9767-A887B3C2A709} = {6756B61E-5856-4CA7-90B5-6053763FE7BA} From 6a1dce540a742e5e44bae0e2faecee339018dda1 Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Wed, 17 Jun 2015 20:50:56 -0400 Subject: [PATCH 2/5] Set near clipping plane distance to 1.0 --- TrueCraft.Client/TrueCraftGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index cb7fbb6..aaa29f2 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -85,7 +85,7 @@ namespace TrueCraft.Client var centerX = GraphicsDevice.Viewport.Width / 2; var centerY = GraphicsDevice.Viewport.Height / 2; Mouse.SetPosition(centerX, centerY); - Camera = new Camera(GraphicsDevice.Viewport.AspectRatio, 70.0f, 0.1f, 1000.0f); + Camera = new Camera(GraphicsDevice.Viewport.AspectRatio, 70.0f, 1.0f, 1000.0f); UpdateCamera(); Window.ClientSizeChanged += (sender, e) => CreateRenderTarget(); MouseComponent.Move += OnMouseComponentMove; From 72e632a07115ec69b351c277e242c0568d7ce10b Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Wed, 17 Jun 2015 21:47:34 -0400 Subject: [PATCH 3/5] Fixed Z-fighting with transparent objects, for now --- TrueCraft.Client/Rendering/Camera.cs | 14 ++++++++++++++ TrueCraft.Client/TrueCraftGame.cs | 13 +++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/TrueCraft.Client/Rendering/Camera.cs b/TrueCraft.Client/Rendering/Camera.cs index 9cd06d2..30f6967 100644 --- a/TrueCraft.Client/Rendering/Camera.cs +++ b/TrueCraft.Client/Rendering/Camera.cs @@ -136,6 +136,20 @@ namespace TrueCraft.Client.Rendering effect.World = Matrix.Identity; } + /// + /// Applies this camera to the specified effect. + /// + /// The effect to apply this camera to. + public void ApplyTo(AlphaTestEffect effect) + { + if (_isDirty) + Recalculate(); + + effect.View = _view; + effect.Projection = _projection; + effect.World = Matrix.Identity; + } + /// /// Returns the bounding frustum calculated for this camera. /// diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index aaa29f2..ca07249 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -42,7 +42,8 @@ namespace TrueCraft.Client private Microsoft.Xna.Framework.Vector3 Delta { get; set; } private TextureMapper TextureMapper { get; set; } - private BasicEffect OpaqueEffect, TransparentEffect; + private BasicEffect OpaqueEffect; + private AlphaTestEffect TransparentEffect; public TrueCraftGame(MultiplayerClient client, IPEndPoint endPoint) { @@ -148,8 +149,9 @@ namespace TrueCraft.Client OpaqueEffect.FogEnd = 1000f; OpaqueEffect.FogColor = Color.CornflowerBlue.ToVector3(); - TransparentEffect = new BasicEffect(GraphicsDevice); - TransparentEffect.TextureEnabled = true; + TransparentEffect = new AlphaTestEffect(GraphicsDevice); + TransparentEffect.AlphaFunction = CompareFunction.Greater; + TransparentEffect.ReferenceAlpha = 127; TransparentEffect.Texture = TextureMapper.GetTexture("terrain.png"); base.LoadContent(); @@ -339,6 +341,7 @@ namespace TrueCraft.Client protected override void Draw(GameTime gameTime) { GraphicsDevice.SetRenderTarget(RenderTarget); + GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; Graphics.GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp; @@ -346,7 +349,7 @@ namespace TrueCraft.Client GraphicsDevice.BlendState = BlendState.NonPremultiplied; int verticies = 0, chunks = 0; - GraphicsDevice.DepthStencilState = new DepthStencilState { DepthBufferEnable = true }; + GraphicsDevice.DepthStencilState = DepthStencilState.Default; for (int i = 0; i < ChunkMeshes.Count; i++) { if (CameraView.Intersects(ChunkMeshes[i].BoundingBox)) @@ -356,7 +359,6 @@ namespace TrueCraft.Client ChunkMeshes[i].Draw(OpaqueEffect, 0); } } - GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; for (int i = 0; i < ChunkMeshes.Count; i++) { if (CameraView.Intersects(ChunkMeshes[i].BoundingBox)) @@ -366,7 +368,6 @@ namespace TrueCraft.Client ChunkMeshes[i].Draw(TransparentEffect, 1); } } - GraphicsDevice.DepthStencilState = DepthStencilState.Default; SpriteBatch.Begin(); for (int i = 0; i < Interfaces.Count; i++) From ba63e7d234bd8240209e9802fe58bdf1376daca2 Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Thu, 18 Jun 2015 08:29:51 -0400 Subject: [PATCH 4/5] Added support for translucent rendering --- TrueCraft.Client/TrueCraftGame.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index ca07249..6cc10aa 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -338,6 +338,15 @@ namespace TrueCraft.Client Camera.ApplyTo(TransparentEffect); } + // Used for faking the disabling of color buffer writing. + private static readonly BlendState ColorWriteDisable = new BlendState() + { + ColorSourceBlend = Blend.Zero, + AlphaSourceBlend = Blend.Zero, + ColorDestinationBlend = Blend.One, + AlphaDestinationBlend = Blend.One + }; + protected override void Draw(GameTime gameTime) { GraphicsDevice.SetRenderTarget(RenderTarget); @@ -346,7 +355,6 @@ namespace TrueCraft.Client Graphics.GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp; GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp; - GraphicsDevice.BlendState = BlendState.NonPremultiplied; int verticies = 0, chunks = 0; GraphicsDevice.DepthStencilState = DepthStencilState.Default; @@ -359,6 +367,19 @@ namespace TrueCraft.Client ChunkMeshes[i].Draw(OpaqueEffect, 0); } } + + GraphicsDevice.BlendState = ColorWriteDisable; + for (int i = 0; i < ChunkMeshes.Count; i++) + { + if (CameraView.Intersects(ChunkMeshes[i].BoundingBox)) + { + if (!ChunkMeshes[i].IsDisposed) + verticies += ChunkMeshes[i].GetTotalVertices(); + ChunkMeshes[i].Draw(TransparentEffect, 1); + } + } + + GraphicsDevice.BlendState = BlendState.NonPremultiplied; for (int i = 0; i < ChunkMeshes.Count; i++) { if (CameraView.Intersects(ChunkMeshes[i].BoundingBox)) From df927e6c0f4f143df865783109b91cda2c8de276 Mon Sep 17 00:00:00 2001 From: William Moorehouse Date: Thu, 18 Jun 2015 11:43:20 -0400 Subject: [PATCH 5/5] Fixed a crash in singleplayer when attempting to render a chunk with no transparent blocks in it --- TrueCraft.Client/Rendering/Mesh.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TrueCraft.Client/Rendering/Mesh.cs b/TrueCraft.Client/Rendering/Mesh.cs index 43d8cd8..9e937ca 100644 --- a/TrueCraft.Client/Rendering/Mesh.cs +++ b/TrueCraft.Client/Rendering/Mesh.cs @@ -150,7 +150,7 @@ namespace TrueCraft.Client.Rendering if ((index < 0) || (index > _indices.Length)) throw new ArgumentOutOfRangeException(); - if (_vertices == null || _vertices.IsDisposed || _indices[index] == null || _indices[index].IsDisposed) + if (_vertices == null || _vertices.IsDisposed || _indices[index] == null || _indices[index].IsDisposed || _indices[index].IndexCount < 3) return; // Invalid state for rendering, just return. effect.GraphicsDevice.SetVertexBuffer(_vertices);