Rebased rendering enhancements and a bug fix
This commit is contained in:
commit
aaddec8951
@ -136,6 +136,20 @@ namespace TrueCraft.Client.Rendering
|
||||
effect.World = Matrix.Identity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies this camera to the specified effect.
|
||||
/// </summary>
|
||||
/// <param name="effect">The effect to apply this camera to.</param>
|
||||
public void ApplyTo(AlphaTestEffect effect)
|
||||
{
|
||||
if (_isDirty)
|
||||
Recalculate();
|
||||
|
||||
effect.View = _view;
|
||||
effect.Projection = _projection;
|
||||
effect.World = Matrix.Identity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bounding frustum calculated for this camera.
|
||||
/// </summary>
|
||||
|
@ -29,6 +29,23 @@ namespace TrueCraft.Client.Rendering
|
||||
Vertices = vertices;
|
||||
SetSubmesh(0, indices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="chunk"></param>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="vertices"></param>
|
||||
/// <param name="opaqueIndices"></param>
|
||||
/// <param name="transparentIndices"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
@ -19,7 +19,7 @@ namespace TrueCraft.Client.Rendering
|
||||
/// A daemon of sorts that creates meshes from chunks.
|
||||
/// Passing meshes back is NOT thread-safe.
|
||||
/// </summary>
|
||||
public class ChunkRenderer
|
||||
public class ChunkRenderer : Renderer<ReadOnlyChunk>
|
||||
{
|
||||
public class ChunkSorter : Comparer<Mesh>
|
||||
{
|
||||
@ -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<MeshGeneratedEventArgs> MeshGenerated;
|
||||
|
||||
private ConcurrentQueue<ReadOnlyChunk> ChunkQueue { get; set; }
|
||||
private GraphicsDevice GraphicsDevice { get; set; }
|
||||
private IBlockRepository BlockRepository { get; set; }
|
||||
private List<Tuple<Thread, CancellationTokenSource>> RenderThreads { get; set; }
|
||||
|
||||
public ChunkRenderer(GraphicsDevice graphics, IBlockRepository blockRepository)
|
||||
: base()
|
||||
{
|
||||
ChunkQueue = new ConcurrentQueue<ReadOnlyChunk>();
|
||||
RenderThreads = new List<Tuple<Thread, CancellationTokenSource>>();
|
||||
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<Thread, CancellationTokenSource>(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<VertexPositionNormalTexture> OpaqueVerticies = new List<VertexPositionNormalTexture>();
|
||||
public readonly List<VertexPositionNormalTexture> Verticies = new List<VertexPositionNormalTexture>();
|
||||
public readonly List<int> OpaqueIndicies = new List<int>();
|
||||
public readonly List<VertexPositionNormalTexture> TransparentVerticies = new List<VertexPositionNormalTexture>();
|
||||
public readonly List<int> TransparentIndicies = new List<int>();
|
||||
public readonly HashSet<Coordinates3D> DrawableCoordinates = new HashSet<Coordinates3D>();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
193
TrueCraft.Client/Rendering/Renderer.cs
Normal file
193
TrueCraft.Client/Rendering/Renderer.cs
Normal file
@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace TrueCraft.Client.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for renderers of meshes.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object to render into a mesh.</typeparam>
|
||||
public abstract class Renderer<T> : IDisposable
|
||||
{
|
||||
private readonly object _syncLock =
|
||||
new object();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public event EventHandler<RendererEventArgs<T>> MeshCompleted;
|
||||
|
||||
private volatile bool _isRunning;
|
||||
private Thread _rendererThread;
|
||||
private ConcurrentQueue<T> _items, _priorityItems;
|
||||
private volatile bool _isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this renderer is running.
|
||||
/// </summary>
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
return _isRunning;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this renderer is disposed of.
|
||||
/// </summary>
|
||||
public bool IsDisposed
|
||||
{
|
||||
get { return _isDisposed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected Renderer()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
_isRunning = false;
|
||||
_rendererThread = new Thread(DoRendering);
|
||||
_items = new ConcurrentQueue<T>(); _priorityItems = new ConcurrentQueue<T>();
|
||||
_isDisposed = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts this renderer.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
if (_isRunning) return;
|
||||
lock (_syncLock)
|
||||
{
|
||||
_isRunning = true;
|
||||
_rendererThread.Start(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
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<T>(item, result, true);
|
||||
if (MeshCompleted != null)
|
||||
MeshCompleted(this, args);
|
||||
}
|
||||
else if (_items.TryDequeue(out item) && TryRender(item, out result))
|
||||
{
|
||||
var args = new RendererEventArgs<T>(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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract bool TryRender(T item, out Mesh result);
|
||||
|
||||
/// <summary>
|
||||
/// Stops this renderer.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (_isDisposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
|
||||
if (!_isRunning) return;
|
||||
lock (_syncLock)
|
||||
{
|
||||
_isRunning = false;
|
||||
_rendererThread.Join();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an item to this renderer for rendering.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="hasPriority"></param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this renderer.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of this renderer.
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
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.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes this renderer.
|
||||
/// </summary>
|
||||
~Renderer()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
39
TrueCraft.Client/Rendering/RendererEventArgs.cs
Normal file
39
TrueCraft.Client/Rendering/RendererEventArgs.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace TrueCraft.Client.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class RendererEventArgs<T> : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public T Item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Mesh Result { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsPriority { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <param name="isPriority"></param>
|
||||
public RendererEventArgs(T item, Mesh result, bool isPriority)
|
||||
{
|
||||
Item = item;
|
||||
Result = result;
|
||||
IsPriority = isPriority;
|
||||
}
|
||||
}
|
||||
}
|
@ -68,6 +68,8 @@
|
||||
<Compile Include="Rendering\Font.cs" />
|
||||
<Compile Include="Rendering\FontRenderer.cs" />
|
||||
<Compile Include="Rendering\FontStyle.cs" />
|
||||
<Compile Include="Rendering\Renderer.cs" />
|
||||
<Compile Include="Rendering\RendererEventArgs.cs" />
|
||||
<Compile Include="Rendering\TextureMapper.cs" />
|
||||
<Compile Include="TrueCraftGame.cs" />
|
||||
<Compile Include="MultiplayerClient.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<ChunkMesh> ChunkMeshes { get; set; }
|
||||
private List<Mesh> ChunkMeshes { get; set; }
|
||||
private ConcurrentBag<Action> PendingMainThreadActions { get; set; }
|
||||
private ConcurrentBag<ChunkMesh> IncomingChunks { get; set; }
|
||||
private ConcurrentBag<ChunkMesh> IncomingTransparentChunks { get; set; }
|
||||
private List<ChunkMesh> TransparentChunkMeshes { get; set; }
|
||||
private ConcurrentBag<Mesh> IncomingChunks { get; set; }
|
||||
public ChatInterface ChatInterface { get; set; }
|
||||
private RenderTarget2D RenderTarget;
|
||||
private BoundingFrustum CameraView;
|
||||
@ -44,23 +42,23 @@ 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)
|
||||
{
|
||||
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<ChunkMesh>();
|
||||
TransparentChunkMeshes = new List<ChunkMesh>();
|
||||
IncomingChunks = new ConcurrentBag<ChunkMesh>();
|
||||
IncomingTransparentChunks = new ConcurrentBag<ChunkMesh>();
|
||||
ChunkMeshes = new List<Mesh>();
|
||||
IncomingChunks = new ConcurrentBag<Mesh>();
|
||||
PendingMainThreadActions = new ConcurrentBag<Action>();
|
||||
MouseCaptured = true;
|
||||
|
||||
@ -79,16 +77,16 @@ 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);
|
||||
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;
|
||||
@ -103,12 +101,9 @@ namespace TrueCraft.Client
|
||||
false, GraphicsDevice.PresentationParameters.BackBufferFormat, DepthFormat.Depth24);
|
||||
}
|
||||
|
||||
void ChunkConverter_MeshGenerated(object sender, ChunkRenderer.MeshGeneratedEventArgs e)
|
||||
void ChunkConverter_MeshGenerated(object sender, RendererEventArgs<ReadOnlyChunk> 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 +114,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;
|
||||
}
|
||||
}
|
||||
@ -154,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();
|
||||
@ -287,16 +283,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();
|
||||
@ -347,37 +338,57 @@ 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);
|
||||
GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
|
||||
|
||||
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 = new DepthStencilState { DepthBufferEnable = true };
|
||||
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
|
||||
for (int i = 0; i < ChunkMeshes.Count; i++)
|
||||
{
|
||||
if (CameraView.Intersects(ChunkMeshes[i].BoundingBox))
|
||||
{
|
||||
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++)
|
||||
|
||||
GraphicsDevice.BlendState = ColorWriteDisable;
|
||||
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.BlendState = BlendState.NonPremultiplied;
|
||||
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.DepthStencilState = DepthStencilState.Default;
|
||||
|
||||
SpriteBatch.Begin();
|
||||
for (int i = 0; i < Interfaces.Count; i++)
|
||||
|
Reference in New Issue
Block a user