Merge pull request #127 from mrpimpunicorn/master

Refactored and expanded Mesh class
This commit is contained in:
Drew DeVault 2015-06-16 15:24:15 -06:00
commit 65b1626e26
3 changed files with 242 additions and 51 deletions

View File

@ -5,19 +5,43 @@ using Microsoft.Xna.Framework;
namespace TrueCraft.Client.Rendering
{
/// <summary>
///
/// </summary>
public class ChunkMesh : Mesh
{
/// <summary>
///
/// </summary>
public ReadOnlyChunk Chunk { get; set; }
public ChunkMesh(ReadOnlyChunk chunk, GraphicsDevice device, VertexPositionNormalTexture[] verticies, int[] indicies)
: base(device, verticies, indicies, false)
/// <summary>
///
/// </summary>
/// <param name="chunk"></param>
/// <param name="device"></param>
/// <param name="vertices"></param>
/// <param name="indices"></param>
public ChunkMesh(ReadOnlyChunk chunk, GraphicsDevice device, VertexPositionNormalTexture[] vertices, int[] indices)
: base(device, 1, true)
{
Chunk = chunk;
BoundingBox = new BoundingBox(
new Vector3(chunk.X * TrueCraft.Core.World.Chunk.Width, 0, chunk.Z * TrueCraft.Core.World.Chunk.Depth),
new Vector3(chunk.X * TrueCraft.Core.World.Chunk.Width
Vertices = vertices;
SetSubmesh(0, indices);
}
/// <summary>
///
/// </summary>
/// <param name="vertices"></param>
/// <returns></returns>
protected override BoundingBox RecalculateBounds(VertexPositionNormalTexture[] vertices)
{
return new BoundingBox(
new Vector3(Chunk.X * TrueCraft.Core.World.Chunk.Width, 0, Chunk.Z * TrueCraft.Core.World.Chunk.Depth),
new Vector3(Chunk.X * TrueCraft.Core.World.Chunk.Width
+ TrueCraft.Core.World.Chunk.Width, TrueCraft.Core.World.Chunk.Height,
chunk.Z * TrueCraft.Core.World.Chunk.Depth + TrueCraft.Core.World.Chunk.Depth));
Chunk.Z * TrueCraft.Core.World.Chunk.Depth + TrueCraft.Core.World.Chunk.Depth));
}
}
}

View File

@ -5,75 +5,242 @@ using System.Linq;
namespace TrueCraft.Client.Rendering
{
/// <summary>
/// Represents an indexed collection of data that can be rendered.
/// </summary>
public class Mesh : IDisposable
{
public bool Empty { get; set; }
public VertexBuffer Verticies { get; set; }
public IndexBuffer Indicies { get; set; }
public BoundingBox BoundingBox { get; set; }
/// <summary>
/// The maximum number of submeshes stored in a single mesh.
/// </summary>
public const int SubmeshLimit = 16;
private static object GraphicsDeviceLock = new object();
// Used for synchronous access to the graphics device.
private static readonly object _syncLock =
new object();
public Mesh(GraphicsDevice device, VertexPositionNormalTexture[] verticies, int[] indicies, bool calculateBounds = true)
private GraphicsDevice _graphicsDevice;
protected VertexBuffer _vertices; // ChunkMesh uses these but external classes shouldn't, so I've made them protected.
protected IndexBuffer[] _indices;
private bool _recalculateBounds; // Whether this mesh should recalculate its bounding box when changed.
/// <summary>
/// Gets or sets the vertices in this mesh.
/// </summary>
public VertexPositionNormalTexture[] Vertices
{
Empty = verticies.Length == 0 && indicies.Length == 0;
if (!Empty)
set
{
lock (GraphicsDeviceLock)
if (_vertices != null)
_vertices.Dispose();
lock (_syncLock)
{
Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration,
verticies.Length, BufferUsage.WriteOnly);
Verticies.SetData(verticies);
Indicies = new IndexBuffer(device, typeof(int), indicies.Length, BufferUsage.WriteOnly);
Indicies.SetData(indicies);
}
if (calculateBounds)
{
BoundingBox = new BoundingBox(
verticies.Select(v => v.Position).OrderBy(v => v.Length()).First(),
verticies.Select(v => v.Position).OrderByDescending(v => v.Length()).First());
_vertices = new VertexBuffer(_graphicsDevice, VertexPositionNormalTexture.VertexDeclaration,
value.Length, BufferUsage.WriteOnly);
_vertices.SetData(value);
}
if (_recalculateBounds)
BoundingBox = RecalculateBounds(value);
}
}
~Mesh()
/// <summary>
/// Gets the bounding box for this mesh.
/// </summary>
public BoundingBox BoundingBox { get; private set; }
/// <summary>
/// Gets whether this mesh is disposed of.
/// </summary>
public bool IsDisposed { get; private set; }
/// <summary>
/// Creates a new mesh.
/// </summary>
/// <param name="graphicsDevice">The graphics device to store the mesh on.</param>
/// <param name="submeshes">The number of submeshes in the mesh.</param>
/// <param name="recalculateBounds">Whether the mesh should recalculate its bounding box when changed.</param>
public Mesh(GraphicsDevice graphicsDevice, int submeshes = 1, bool recalculateBounds = true)
{
Dispose(false);
if (graphicsDevice == null)
throw new ArgumentException();
if ((submeshes < 0) || (submeshes >= Mesh.SubmeshLimit))
throw new ArgumentOutOfRangeException();
_graphicsDevice = graphicsDevice;
_indices = new IndexBuffer[submeshes];
_recalculateBounds = recalculateBounds;
}
public void Dispose()
/// <summary>
/// Creates a new mesh.
/// </summary>
/// <param name="graphicsDevice">The graphics device to store the mesh on.</param>
/// <param name="vertices">The vertices in the mesh.</param>
/// <param name="submeshes">The number of submeshes in the mesh.</param>
/// <param name="recalculateBounds">Whether the mesh should recalculate its bounding box when changed.</param>
public Mesh(GraphicsDevice graphicsDevice, VertexPositionNormalTexture[] vertices, int submeshes = 1, bool recalculateBounds = true)
: this(graphicsDevice, submeshes, recalculateBounds)
{
Dispose(true);
GC.SuppressFinalize(this);
Vertices = vertices;
}
protected virtual void Dispose(bool disposing)
/// <summary>
/// Creates a new mesh.
/// </summary>
/// <param name="graphicsDevice">The graphics device to store the mesh on.</param>
/// <param name="vertices">The vertices in the mesh.</param>
/// <param name="indices">The first (and only) submesh in the mesh.</param>
/// <param name="recalculateBounds">Whether the mesh should recalculate its bounding box when changed.</param>
public Mesh(GraphicsDevice graphicsDevice, VertexPositionNormalTexture[] vertices, int[] indices, bool recalculateBounds = true)
: this(graphicsDevice, 1, recalculateBounds)
{
if (disposing)
Vertices = vertices;
SetSubmesh(0, indices);
}
/// <summary>
/// Sets a submesh in this mesh.
/// </summary>
/// <param name="index">The submesh index.</param>
/// <param name="data">The indices for the submesh.</param>
public void SetSubmesh(int index, int[] indices)
{
if ((index < 0) || (index > _indices.Length))
throw new ArgumentOutOfRangeException();
lock (_syncLock)
{
if (Verticies != null)
Verticies.Dispose();
if (Indicies != null)
Indicies.Dispose();
}
if (_indices[index] != null)
_indices[index].Dispose();
Verticies = null;
Indicies = null;
_indices[index] = new IndexBuffer(_graphicsDevice, typeof(int),
indices.Length, BufferUsage.WriteOnly);
_indices[index].SetData(indices);
}
}
/// <summary>
/// Draws this mesh using the specified effect.
/// </summary>
/// <param name="effect">The effect to use.</param>
public void Draw(Effect effect)
{
if (Empty)
return;
effect.GraphicsDevice.SetVertexBuffer(Verticies);
effect.GraphicsDevice.Indices = Indicies;
if (effect == null)
throw new ArgumentException();
for (int i = 0; i < _indices.Length; i++)
Draw(effect, i);
}
/// <summary>
/// Draws a submesh in this mesh using the specified effect.
/// </summary>
/// <param name="effect">The effect to use.</param>
/// <param name="index">The submesh index.</param>
public void Draw(Effect effect, int index)
{
if (effect == null)
throw new ArgumentException();
if ((index < 0) || (index > _indices.Length))
throw new ArgumentOutOfRangeException();
if (_vertices == null || _vertices.IsDisposed || _indices[index] == null || _indices[index].IsDisposed)
return; // Invalid state for rendering, just return.
effect.GraphicsDevice.SetVertexBuffer(_vertices);
effect.GraphicsDevice.Indices = _indices[index];
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
0, 0, Indicies.IndexCount, 0, Indicies.IndexCount / 3);
0, 0, _indices[index].IndexCount, 0, _indices[index].IndexCount / 3);
}
}
/// <summary>
/// Returns the total vertices in this mesh.
/// </summary>
/// <returns></returns>
public int GetTotalVertices()
{
if (_vertices == null)
return 0;
lock (_syncLock)
return _vertices.VertexCount;
}
/// <summary>
/// Returns the total indices for all the submeshes in this mesh.
/// </summary>
/// <returns></returns>
public int GetTotalIndices()
{
lock (_syncLock)
{
int sum = 0;
foreach (var element in _indices)
sum += (element != null) ? element.IndexCount : 0;
return sum;
}
}
/// <summary>
/// Recalculates the bounding box for this mesh.
/// </summary>
/// <param name="vertices">The vertices in this mesh.</param>
/// <returns></returns>
protected virtual BoundingBox RecalculateBounds(VertexPositionNormalTexture[] vertices)
{
return new BoundingBox(
vertices.Select(v => v.Position).OrderBy(v => v.Length()).First(),
vertices.Select(v => v.Position).OrderByDescending(v => v.Length()).First());
}
/// <summary>
/// Disposes of this mesh.
/// </summary>
public void Dispose()
{
if (IsDisposed)
return;
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes of this mesh.
/// </summary>
/// <param name="disposing">Whether Dispose() called the method.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_graphicsDevice = null; // Lose the reference to our graphics device.
if (_vertices != null)
_vertices.Dispose();
foreach (var element in _indices)
{
if (element != null)
element.Dispose();
}
}
}
/// <summary>
/// Finalizes this mesh.
/// </summary>
~Mesh()
{
Dispose(false);
}
}
}

View File

@ -350,9 +350,9 @@ namespace TrueCraft.Client
GraphicsDevice.DepthStencilState = new DepthStencilState { DepthBufferEnable = true };
for (int i = 0; i < ChunkMeshes.Count; i++)
{
if (CameraView.Intersects(ChunkMeshes[i].BoundingBox) && !ChunkMeshes[i].Empty)
if (CameraView.Intersects(ChunkMeshes[i].BoundingBox))
{
verticies += ChunkMeshes[i].Verticies.VertexCount;
verticies += ChunkMeshes[i].GetTotalVertices();
chunks++;
ChunkMeshes[i].Draw(OpaqueEffect);
}
@ -360,10 +360,10 @@ namespace TrueCraft.Client
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
for (int i = 0; i < TransparentChunkMeshes.Count; i++)
{
if (CameraView.Intersects(TransparentChunkMeshes[i].BoundingBox) && !TransparentChunkMeshes[i].Empty)
if (CameraView.Intersects(TransparentChunkMeshes[i].BoundingBox))
{
if (TransparentChunkMeshes[i].Verticies != null)
verticies += TransparentChunkMeshes[i].Verticies.VertexCount;
if (!TransparentChunkMeshes[i].IsDisposed)
verticies += TransparentChunkMeshes[i].GetTotalVertices();
TransparentChunkMeshes[i].Draw(TransparentEffect);
}
}