2015-05-13 17:02:03 -06:00
|
|
|
|
using System;
|
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
2015-05-13 23:09:49 -06:00
|
|
|
|
using Microsoft.Xna.Framework;
|
2015-05-15 15:48:20 -06:00
|
|
|
|
using System.Linq;
|
2015-05-13 17:02:03 -06:00
|
|
|
|
|
2015-05-16 21:50:10 -06:00
|
|
|
|
namespace TrueCraft.Client.Rendering
|
2015-05-13 17:02:03 -06:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Represents an indexed collection of data that can be rendered.
|
|
|
|
|
/// </summary>
|
2015-05-25 08:05:38 -07:00
|
|
|
|
public class Mesh : IDisposable
|
2015-05-13 17:02:03 -06:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The maximum number of submeshes stored in a single mesh.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const int SubmeshLimit = 16;
|
2015-05-13 17:02:03 -06:00
|
|
|
|
|
2015-06-16 15:53:28 -04:00
|
|
|
|
// Used for synchronous access to the graphics device.
|
|
|
|
|
private static readonly object _syncLock =
|
|
|
|
|
new object();
|
2015-06-03 16:12:43 -06:00
|
|
|
|
|
2015-06-19 00:55:38 -04:00
|
|
|
|
private TrueCraftGame _game;
|
2015-06-16 15:53:28 -04:00
|
|
|
|
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>
|
2015-06-19 17:36:39 -04:00
|
|
|
|
public VertexPositionNormalColorTexture[] Vertices
|
2015-05-13 17:02:03 -06:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
set
|
2015-05-14 18:47:16 -06:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
if (_vertices != null)
|
|
|
|
|
_vertices.Dispose();
|
|
|
|
|
|
2015-06-19 01:11:14 -04:00
|
|
|
|
_game.PendingMainThreadActions.Add(() =>
|
2015-05-15 15:48:20 -06:00
|
|
|
|
{
|
2015-06-19 17:36:39 -04:00
|
|
|
|
_vertices = new VertexBuffer(_graphicsDevice, VertexPositionNormalColorTexture.VertexDeclaration,
|
2015-06-17 13:24:39 -04:00
|
|
|
|
(value.Length + 1), BufferUsage.WriteOnly);
|
2015-06-16 15:53:28 -04:00
|
|
|
|
_vertices.SetData(value);
|
2015-06-19 01:11:14 -04:00
|
|
|
|
});
|
2015-06-16 15:53:28 -04:00
|
|
|
|
|
|
|
|
|
if (_recalculateBounds)
|
|
|
|
|
BoundingBox = RecalculateBounds(value);
|
2015-05-14 18:47:16 -06:00
|
|
|
|
}
|
2015-05-13 17:02:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <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>
|
2015-06-19 00:55:38 -04:00
|
|
|
|
public Mesh(TrueCraftGame game, int submeshes = 1, bool recalculateBounds = true)
|
2015-05-13 17:02:03 -06:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
if ((submeshes < 0) || (submeshes >= Mesh.SubmeshLimit))
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
|
2015-06-19 00:55:38 -04:00
|
|
|
|
_game = game;
|
|
|
|
|
_graphicsDevice = game.GraphicsDevice;
|
2015-06-16 15:53:28 -04:00
|
|
|
|
_indices = new IndexBuffer[submeshes];
|
|
|
|
|
_recalculateBounds = recalculateBounds;
|
2015-05-25 08:05:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <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>
|
2015-06-19 17:36:39 -04:00
|
|
|
|
public Mesh(TrueCraftGame game, VertexPositionNormalColorTexture[] vertices, int submeshes = 1, bool recalculateBounds = true)
|
2015-06-19 00:55:38 -04:00
|
|
|
|
: this(game, submeshes, recalculateBounds)
|
2015-05-25 08:05:38 -07:00
|
|
|
|
{
|
2015-06-19 01:11:14 -04:00
|
|
|
|
Vertices = vertices;
|
2015-06-16 15:53:28 -04:00
|
|
|
|
}
|
2015-05-25 08:05:38 -07:00
|
|
|
|
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <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>
|
2015-06-19 17:36:39 -04:00
|
|
|
|
public Mesh(TrueCraftGame game, VertexPositionNormalColorTexture[] vertices, int[] indices, bool recalculateBounds = true)
|
2015-06-19 00:55:38 -04:00
|
|
|
|
: this(game, 1, recalculateBounds)
|
2015-06-16 15:53:28 -04:00
|
|
|
|
{
|
2015-06-19 01:11:14 -04:00
|
|
|
|
Vertices = vertices;
|
2015-06-16 15:53:28 -04:00
|
|
|
|
SetSubmesh(0, indices);
|
2015-05-25 08:05:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <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)
|
2015-05-25 08:05:38 -07:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
if ((index < 0) || (index > _indices.Length))
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
|
|
|
|
|
lock (_syncLock)
|
2015-05-25 08:05:38 -07:00
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
if (_indices[index] != null)
|
|
|
|
|
_indices[index].Dispose();
|
2015-05-25 08:05:38 -07:00
|
|
|
|
|
2015-06-19 01:11:14 -04:00
|
|
|
|
_game.PendingMainThreadActions.Add(() =>
|
|
|
|
|
{
|
|
|
|
|
_indices[index] = new IndexBuffer(_graphicsDevice, typeof(int),
|
|
|
|
|
(indices.Length + 1), BufferUsage.WriteOnly);
|
|
|
|
|
_indices[index].SetData(indices);
|
|
|
|
|
});
|
2015-06-16 15:53:28 -04:00
|
|
|
|
}
|
2015-05-13 17:02:03 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-16 15:53:28 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Draws this mesh using the specified effect.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="effect">The effect to use.</param>
|
2015-05-13 17:02:03 -06:00
|
|
|
|
public void Draw(Effect effect)
|
|
|
|
|
{
|
2015-06-16 15:53:28 -04:00
|
|
|
|
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();
|
|
|
|
|
|
2015-06-18 11:43:20 -04:00
|
|
|
|
if (_vertices == null || _vertices.IsDisposed || _indices[index] == null || _indices[index].IsDisposed || _indices[index].IndexCount < 3)
|
2015-06-16 15:53:28 -04:00
|
|
|
|
return; // Invalid state for rendering, just return.
|
|
|
|
|
|
|
|
|
|
effect.GraphicsDevice.SetVertexBuffer(_vertices);
|
|
|
|
|
effect.GraphicsDevice.Indices = _indices[index];
|
2015-05-13 17:02:03 -06:00
|
|
|
|
foreach (var pass in effect.CurrentTechnique.Passes)
|
|
|
|
|
{
|
|
|
|
|
pass.Apply();
|
|
|
|
|
effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
|
2015-06-16 15:53:28 -04:00
|
|
|
|
0, 0, _indices[index].IndexCount, 0, _indices[index].IndexCount / 3);
|
2015-05-13 17:02:03 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-16 15:53:28 -04:00
|
|
|
|
|
|
|
|
|
/// <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>
|
2015-06-19 17:36:39 -04:00
|
|
|
|
protected virtual BoundingBox RecalculateBounds(VertexPositionNormalColorTexture[] vertices)
|
2015-06-16 15:53:28 -04:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2015-05-13 17:02:03 -06:00
|
|
|
|
}
|
|
|
|
|
}
|