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 namespace TrueCraft.Client.Rendering
{ {
/// <summary>
///
/// </summary>
public class ChunkMesh : Mesh public class ChunkMesh : Mesh
{ {
/// <summary>
///
/// </summary>
public ReadOnlyChunk Chunk { get; set; } public ReadOnlyChunk Chunk { get; set; }
public ChunkMesh(ReadOnlyChunk chunk, GraphicsDevice device, VertexPositionNormalTexture[] verticies, int[] indicies) /// <summary>
: base(device, verticies, indicies, false) ///
/// </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; Chunk = chunk;
BoundingBox = new BoundingBox( Vertices = vertices;
new Vector3(chunk.X * TrueCraft.Core.World.Chunk.Width, 0, chunk.Z * TrueCraft.Core.World.Chunk.Depth), SetSubmesh(0, indices);
new Vector3(chunk.X * TrueCraft.Core.World.Chunk.Width }
/// <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, + 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 namespace TrueCraft.Client.Rendering
{ {
/// <summary>
/// Represents an indexed collection of data that can be rendered.
/// </summary>
public class Mesh : IDisposable public class Mesh : IDisposable
{ {
public bool Empty { get; set; } /// <summary>
public VertexBuffer Verticies { get; set; } /// The maximum number of submeshes stored in a single mesh.
public IndexBuffer Indicies { get; set; } /// </summary>
public BoundingBox BoundingBox { get; set; } 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; set
if (!Empty)
{ {
lock (GraphicsDeviceLock) if (_vertices != null)
_vertices.Dispose();
lock (_syncLock)
{ {
Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, _vertices = new VertexBuffer(_graphicsDevice, VertexPositionNormalTexture.VertexDeclaration,
verticies.Length, BufferUsage.WriteOnly); value.Length, BufferUsage.WriteOnly);
Verticies.SetData(verticies); _vertices.SetData(value);
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());
} }
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); Vertices = vertices;
GC.SuppressFinalize(this);
} }
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) if (_indices[index] != null)
Verticies.Dispose(); _indices[index].Dispose();
if (Indicies != null)
Indicies.Dispose();
}
Verticies = null; _indices[index] = new IndexBuffer(_graphicsDevice, typeof(int),
Indicies = null; 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) public void Draw(Effect effect)
{ {
if (Empty) if (effect == null)
return; throw new ArgumentException();
effect.GraphicsDevice.SetVertexBuffer(Verticies);
effect.GraphicsDevice.Indices = Indicies; 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) foreach (var pass in effect.CurrentTechnique.Passes)
{ {
pass.Apply(); pass.Apply();
effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 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 }; GraphicsDevice.DepthStencilState = new DepthStencilState { DepthBufferEnable = true };
for (int i = 0; i < ChunkMeshes.Count; i++) 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++; chunks++;
ChunkMeshes[i].Draw(OpaqueEffect); ChunkMeshes[i].Draw(OpaqueEffect);
} }
@ -360,10 +360,10 @@ namespace TrueCraft.Client
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead; GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
for (int i = 0; i < TransparentChunkMeshes.Count; i++) 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) if (!TransparentChunkMeshes[i].IsDisposed)
verticies += TransparentChunkMeshes[i].Verticies.VertexCount; verticies += TransparentChunkMeshes[i].GetTotalVertices();
TransparentChunkMeshes[i].Draw(TransparentEffect); TransparentChunkMeshes[i].Draw(TransparentEffect);
} }
} }