Merge pull request #127 from mrpimpunicorn/master
Refactored and expanded Mesh class
This commit is contained in:
commit
65b1626e26
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user