mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-24 12:06:37 +00:00
* Fix BoundingRect unit tests. * Create `graphics` library.
310 lines
12 KiB
C#
310 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace MonoGame.Extended.Graphics
|
|
{
|
|
internal enum GeometryBufferType
|
|
{
|
|
Static,
|
|
Dynamic
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stores geometry which is to be uploaded to a <see cref="GraphicsDevice" />.
|
|
/// </summary>
|
|
/// <typeparam name="TVertexType">The type of the vertex type.</typeparam>
|
|
/// <seealso cref="IDisposable" />
|
|
/// <remarks>
|
|
/// <para>
|
|
/// For best performance, use <see cref="DynamicGeometryBuffer{TVertexType}" /> for geometry which changes
|
|
/// frame-to-frame and <see cref="StaticGeometryBuffer{TVertexType}" /> for geoemtry which does not change
|
|
/// frame-to-frame, or changes infrequently between frames.
|
|
/// </para>
|
|
/// </remarks>
|
|
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
|
|
public abstract class GeometryBuffer<TVertexType> : IDisposable
|
|
where TVertexType : struct, IVertexType
|
|
{
|
|
internal readonly GeometryBufferType BufferType;
|
|
|
|
internal GeometryBuffer(GraphicsDevice graphicsDevice, GeometryBufferType bufferType,
|
|
ushort maximumVerticesCount, ushort maximumIndicesCount)
|
|
{
|
|
if (graphicsDevice == null)
|
|
throw new ArgumentNullException(nameof(graphicsDevice));
|
|
|
|
if (maximumVerticesCount <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(maximumVerticesCount));
|
|
|
|
if (maximumIndicesCount <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(maximumIndicesCount));
|
|
|
|
if ((bufferType != GeometryBufferType.Static) && (bufferType != GeometryBufferType.Dynamic))
|
|
throw new ArgumentOutOfRangeException(nameof(bufferType));
|
|
|
|
GraphicsDevice = graphicsDevice;
|
|
|
|
_vertices = new TVertexType[maximumVerticesCount];
|
|
_indices = new ushort[maximumIndicesCount];
|
|
_vertexCount = 0;
|
|
MaximumVerticesCount = maximumVerticesCount;
|
|
MaximumIndicesCount = maximumIndicesCount;
|
|
|
|
BufferType = bufferType;
|
|
|
|
switch (bufferType)
|
|
{
|
|
case GeometryBufferType.Static:
|
|
VertexBuffer = new VertexBuffer(graphicsDevice, typeof(TVertexType), maximumVerticesCount,
|
|
BufferUsage.WriteOnly);
|
|
IndexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort), maximumIndicesCount,
|
|
BufferUsage.WriteOnly);
|
|
break;
|
|
case GeometryBufferType.Dynamic:
|
|
VertexBuffer = new DynamicVertexBuffer(graphicsDevice, typeof(TVertexType), maximumVerticesCount,
|
|
BufferUsage.WriteOnly);
|
|
IndexBuffer = new DynamicIndexBuffer(graphicsDevice, typeof(ushort), maximumIndicesCount,
|
|
BufferUsage.WriteOnly);
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(bufferType));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the vertices as read-only.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The vertices as read-only.
|
|
/// </value>
|
|
public IReadOnlyList<TVertexType> Vertices => _vertices;
|
|
|
|
/// <summary>
|
|
/// Gets the indices as read-only.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The indices as read-only.
|
|
/// </value>
|
|
public IReadOnlyList<ushort> Indices => _indices;
|
|
|
|
/// <summary>
|
|
/// Gets the number of buffered vertices.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The number of buffered vertices.
|
|
/// </value>
|
|
public ushort VertexCount => _vertexCount;
|
|
|
|
/// <summary>
|
|
/// Gets the number of buffered indices.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The number of buffered indices.
|
|
/// </value>
|
|
public ushort IndexCount => _indexCount;
|
|
|
|
public ushort MaximumVerticesCount { get; }
|
|
public ushort MaximumIndicesCount { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the vertex buffer.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The vertex buffer.
|
|
/// </value>
|
|
public VertexBuffer VertexBuffer { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the index buffer.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The index buffer.
|
|
/// </value>
|
|
public IndexBuffer IndexBuffer { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="GraphicsDevice" /> associated with this
|
|
/// <see cref="GeometryBuffer{TVertexType}" />.
|
|
/// </summary>
|
|
/// <value>
|
|
/// The <see cref="GraphicsDevice" /> associated with this
|
|
/// <see cref="GeometryBuffer{TVertexType}" />.
|
|
/// </value>
|
|
public GraphicsDevice GraphicsDevice { get; }
|
|
|
|
/// <summary>
|
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uploads the buffered geometry to the <see cref="GraphicsDevice" />.
|
|
/// </summary>
|
|
public void Flush()
|
|
{
|
|
VertexBuffer.SetData(_vertices, 0, _vertexCount);
|
|
IndexBuffer.SetData(_indices, 0, _indexCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the specified vertex to the buffer of geometry.
|
|
/// </summary>
|
|
/// <param name="vertex">The vertex.</param>
|
|
public bool Enqueue(ref TVertexType vertex)
|
|
{
|
|
ThrowIfWouldOverflowVertices(1);
|
|
|
|
_vertices[_vertexCount++] = vertex;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the specified vertex index to the buffer of geometry.
|
|
/// </summary>
|
|
/// <param name="index">The vertex index.</param>
|
|
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
|
public void Enqueue(ushort index)
|
|
{
|
|
ThrowIfWouldOverflowIndices(1);
|
|
_indices[_indexCount++] = index;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to add the specified vertices to the buffer of geometry.
|
|
/// </summary>
|
|
/// <param name="vertices">The vertices.</param>
|
|
/// <param name="sourceIndex">
|
|
/// The value that represents the index in the <paramref name="vertices" /> at which adding
|
|
/// begins.
|
|
/// </param>
|
|
/// <param name="length">The number of vertices to add.</param>
|
|
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
|
public void Enqueue(TVertexType[] vertices, int sourceIndex, ushort length)
|
|
{
|
|
ThrowIfWouldOverflowVertices(length);
|
|
|
|
Array.Copy(vertices, sourceIndex, _vertices, VertexCount, length);
|
|
_vertexCount += length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds vertex indices to the buffer of geometry.
|
|
/// </summary>
|
|
/// <param name="indices">The indices.</param>
|
|
/// <param name="sourceIndex">
|
|
/// The value that represents the index in the <paramref name="indices" /> at which adding
|
|
/// begins.
|
|
/// </param>
|
|
/// <param name="length">The number of indices to add.</param>
|
|
/// <param name="indexOffset">The index offset.</param>
|
|
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
|
public unsafe void Enqueue(ushort[] indices, int sourceIndex, ushort length, ushort indexOffset)
|
|
{
|
|
ThrowIfWouldOverflowIndices(length);
|
|
|
|
var start = _indexCount;
|
|
var end = _indexCount + length;
|
|
|
|
Array.Copy(indices, sourceIndex, _indices, IndexCount, length);
|
|
|
|
fixed (ushort* fixedPointer = _indices)
|
|
{
|
|
var pointer = fixedPointer + start;
|
|
var endPointer = fixedPointer + end;
|
|
|
|
while (pointer < endPointer)
|
|
{
|
|
*pointer += indexOffset;
|
|
++pointer;
|
|
}
|
|
}
|
|
|
|
_indexCount += length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds clockwise quadrilateral indices to the buffer of geometry.
|
|
/// </summary>
|
|
/// <param name="indexOffset">The index offset.</param>
|
|
/// <exception cref="GeometryBufferOverflowException{TVertexType}">The <see cref="GeometryBuffer{TVertexType}" /> is full.</exception>
|
|
public unsafe void EnqueueClockwiseQuadrilateralIndices(ushort indexOffset)
|
|
{
|
|
ThrowIfWouldOverflowIndices(6);
|
|
|
|
fixed (ushort* fixedPointer = _indices)
|
|
{
|
|
var pointer = fixedPointer + _indexCount;
|
|
*(pointer + 0) = (ushort) (0 + indexOffset);
|
|
*(pointer + 1) = (ushort) (1 + indexOffset);
|
|
*(pointer + 2) = (ushort) (2 + indexOffset);
|
|
*(pointer + 3) = (ushort) (1 + indexOffset);
|
|
*(pointer + 4) = (ushort) (3 + indexOffset);
|
|
*(pointer + 5) = (ushort) (2 + indexOffset);
|
|
}
|
|
_indexCount += 6;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the buffered geometry count.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
_vertexCount = 0;
|
|
_indexCount = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases unmanaged and - optionally - managed resources.
|
|
/// </summary>
|
|
/// <param name="disposing">
|
|
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
|
|
/// unmanaged resources.
|
|
/// </param>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!disposing)
|
|
return;
|
|
|
|
VertexBuffer.Dispose();
|
|
IndexBuffer.Dispose();
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void ThrowIfWouldOverflowVertices(ushort verticesCountToAdd)
|
|
{
|
|
if (_vertexCount + verticesCountToAdd > MaximumVerticesCount)
|
|
throw new GeometryBufferOverflowException<TVertexType>(this, verticesCountToAdd, 0);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void ThrowIfWouldOverflowIndices(ushort indicesCountToAdd)
|
|
{
|
|
if (_indexCount + indicesCountToAdd > MaximumIndicesCount)
|
|
throw new GeometryBufferOverflowException<TVertexType>(this, 0, indicesCountToAdd);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
internal void ThrowIfWouldOverflow(ushort verticesCountToAdd, ushort indicesCountToAdd)
|
|
{
|
|
if ((_vertexCount + verticesCountToAdd > MaximumVerticesCount) ||
|
|
(_indexCount + indicesCountToAdd > MaximumIndicesCount))
|
|
throw new GeometryBufferOverflowException<TVertexType>(this, verticesCountToAdd, indicesCountToAdd);
|
|
}
|
|
|
|
//TODO: Add TIndexType to allow for different type of indices: int, short, etc
|
|
|
|
// ReSharper disable InconsistentNaming
|
|
internal readonly TVertexType[] _vertices;
|
|
internal readonly ushort[] _indices;
|
|
internal ushort _vertexCount;
|
|
internal ushort _indexCount;
|
|
// ReSharper restore InconsistentNaming
|
|
}
|
|
} |