From acea7b2e728787a0d83bbf83c8c1f042d2c32e7e Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Mon, 3 Jun 2024 10:15:45 +0800
Subject: + plugins project
---
.../source/MonoGame.Extended.Graphics/Batcher2D.cs | 450 +++++++++++++++++++++
1 file changed, 450 insertions(+)
create mode 100644 Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs
(limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs')
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs
new file mode 100644
index 0000000..e7ec533
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher2D.cs
@@ -0,0 +1,450 @@
+using System;
+using System.Text;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using MonoGame.Extended.BitmapFonts;
+using MonoGame.Extended.Graphics.Effects;
+using MonoGame.Extended.Graphics.Geometry;
+
+namespace MonoGame.Extended.Graphics
+{
+ ///
+ /// A general purpose for two-dimensional geometry that change
+ /// frequently between frames such as sprites and shapes.
+ ///
+ ///
+ ///
+ /// For drawing user interfaces, consider using instead because it supports scissor rectangles.
+ ///
+ public sealed class Batcher2D : Batcher
+ {
+
+ internal const int DefaultMaximumVerticesCount = 8192;
+ internal const int DefaultMaximumIndicesCount = 12288;
+
+ private readonly VertexBuffer _vertexBuffer;
+ private readonly IndexBuffer _indexBuffer;
+ private readonly VertexPositionColorTexture[] _vertices;
+ private int _vertexCount;
+ private readonly ushort[] _indices;
+ private int _indexCount;
+ private readonly ushort[] _sortedIndices;
+ private readonly GeometryBuilder2D _geometryBuilder;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The graphics device.
+ ///
+ /// The maximum number of vertices that can be enqueued before a
+ /// is required. The default value is 8192.
+ ///
+ ///
+ /// The maximum number of indices that can be enqueued before a
+ /// is required. The default value is 12288.
+ ///
+ ///
+ /// The maximum number of structs that can be enqueued before a
+ /// is required. The default value is 2048.
+ ///
+ /// .
+ ///
+ /// is less than or equal
+ /// 0, or is less than or equal to 0, or,
+ /// is less than or equal to 0.
+ ///
+ public Batcher2D(GraphicsDevice graphicsDevice,
+ ushort maximumVerticesCount = DefaultMaximumVerticesCount,
+ ushort maximumIndicesCount = DefaultMaximumIndicesCount,
+ int maximumDrawCallsCount = DefaultBatchMaximumDrawCallsCount)
+ : base(
+ graphicsDevice,
+ new DefaultEffect(graphicsDevice)
+ {
+ TextureEnabled = true,
+ VertexColorEnabled = true
+ }, maximumDrawCallsCount)
+
+ {
+ _vertices = new VertexPositionColorTexture[maximumVerticesCount];
+ _vertexBuffer = new DynamicVertexBuffer(graphicsDevice, VertexPositionColorTexture.VertexDeclaration, maximumVerticesCount, BufferUsage.WriteOnly);
+
+ _indices = new ushort[maximumIndicesCount];
+ _sortedIndices = new ushort[maximumIndicesCount];
+ _indexBuffer = new DynamicIndexBuffer(graphicsDevice, IndexElementSize.SixteenBits, maximumIndicesCount, BufferUsage.WriteOnly);
+ _geometryBuilder = new GeometryBuilder2D(4, 6);
+ }
+
+ protected override void SortDrawCallsAndBindBuffers()
+ {
+ // Upload the vertices to the GPU and then select that vertex stream for drawing
+ _vertexBuffer.SetData(_vertices, 0, _vertexCount);
+ GraphicsDevice.SetVertexBuffer(_vertexBuffer);
+
+ Array.Sort(DrawCalls, 0, EnqueuedDrawCallCount);
+ BuildSortedIndices();
+
+ // Upload the indices to the GPU and then select that index stream for drawing
+ _indexBuffer.SetData(_sortedIndices, 0, _indexCount);
+ GraphicsDevice.Indices = _indexBuffer;
+
+ _indexCount = 0;
+ _vertexCount = 0;
+ }
+
+ private void BuildSortedIndices()
+ {
+ var newDrawCallsCount = 0;
+ DrawCalls[0].StartIndex = 0;
+ var currentDrawCall = DrawCalls[0];
+ DrawCalls[newDrawCallsCount++] = DrawCalls[0];
+
+ var drawCallIndexCount = currentDrawCall.PrimitiveCount * 3;
+ Array.Copy(_indices, currentDrawCall.StartIndex, _sortedIndices, 0, drawCallIndexCount);
+ var sortedIndexCount = drawCallIndexCount;
+
+ // iterate through sorted draw calls checking if any can now be merged to reduce expensive draw calls to the graphics API
+ // this might need to be changed for next-gen graphics API (Vulkan, Metal, DirectX 12) where the draw calls are not so expensive
+ for (var i = 1; i < EnqueuedDrawCallCount; i++)
+ {
+ currentDrawCall = DrawCalls[i];
+ drawCallIndexCount = currentDrawCall.PrimitiveCount * 3;
+ Array.Copy(_indices, currentDrawCall.StartIndex, _sortedIndices, sortedIndexCount, drawCallIndexCount);
+ sortedIndexCount += drawCallIndexCount;
+
+ if (currentDrawCall.TryMerge(ref DrawCalls[newDrawCallsCount - 1]))
+ continue;
+
+ currentDrawCall.StartIndex = sortedIndexCount;
+ DrawCalls[newDrawCallsCount++] = currentDrawCall;
+ }
+
+ EnqueuedDrawCallCount = newDrawCallsCount;
+ }
+
+ ///
+ /// Submits a draw operation to the using the specified .
+ ///
+ /// The draw call information.
+ protected override void InvokeDrawCall(ref DrawCallInfo drawCall)
+ {
+ GraphicsDevice.DrawIndexedPrimitives(drawCall.PrimitiveType, 0, drawCall.StartIndex, drawCall.PrimitiveCount);
+ }
+
+ ///
+ /// Draws a sprite using a specified , transform , source
+ /// , and an optional
+ /// , origin , , and depth .
+ ///
+ /// The .
+ /// The transform .
+ ///
+ /// The texture region of the . Use
+ /// null to use the entire .
+ ///
+ /// The . Use null to use the default .
+ /// The . The default value is .
+ /// The depth . The default value is 0.
+ /// The method has not been called.
+ /// is null.
+ public void DrawSprite(Texture2D texture, ref Matrix2 transformMatrix, ref Rectangle sourceRectangle,
+ Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0)
+ {
+ _geometryBuilder.BuildSprite(_vertexCount, ref transformMatrix, texture, ref sourceRectangle, color, flags, depth);
+ EnqueueBuiltGeometry(texture, depth);
+ }
+
+ ///
+ /// Draws a using the specified transform and an optional
+ /// , origin , , and depth .
+ ///
+ /// The .
+ /// The transform .
+ /// The . Use null to use the default .
+ /// The . The default value is .
+ /// The depth . The default value is 0.
+ /// The method has not been called.
+ /// is null.
+ public void DrawTexture(Texture2D texture, ref Matrix2 transformMatrix, Color? color = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0)
+ {
+ var rectangle = default(Rectangle);
+ _geometryBuilder.BuildSprite(_vertexCount, ref transformMatrix, texture, ref rectangle, color, flags, depth);
+ EnqueueBuiltGeometry(texture, depth);
+ }
+
+ private void EnqueueBuiltGeometry(Texture2D texture, float depth)
+ {
+ if ((_vertexCount + _geometryBuilder.VertexCount > _vertices.Length) ||
+ (_indexCount + _geometryBuilder.IndexCount > _indices.Length))
+ Flush();
+ var drawCall = new DrawCallInfo(texture, _geometryBuilder.PrimitiveType, _indexCount,
+ _geometryBuilder.PrimitivesCount, depth);
+ Array.Copy(_geometryBuilder.Vertices, 0, _vertices, _vertexCount, _geometryBuilder.VertexCount);
+ _vertexCount += _geometryBuilder.VertexCount;
+ Array.Copy(_geometryBuilder.Indices, 0, _indices, _indexCount, _geometryBuilder.IndexCount);
+ _indexCount += _geometryBuilder.IndexCount;
+ Enqueue(ref drawCall);
+ }
+
+ ///
+ /// Draws unicode (UTF-16) characters as sprites using the specified , text
+ /// , transform and optional , origin
+ /// , , and depth .
+ ///
+ /// The .
+ /// The text .
+ /// The transform .
+ ///
+ /// The . Use null to use the default
+ /// .
+ ///
+ /// The . The default value is .
+ /// The depth . The default value is 0f.
+ /// The method has not been called.
+ /// is null or is null.
+ public void DrawString(BitmapFont bitmapFont, StringBuilder text, ref Matrix2 transformMatrix,
+ Color? color = null, FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ EnsureHasBegun();
+
+ if (bitmapFont == null)
+ throw new ArgumentNullException(nameof(bitmapFont));
+
+ if (text == null)
+ throw new ArgumentNullException(nameof(text));
+
+ var lineSpacing = bitmapFont.LineHeight;
+ var offset = new Vector2(0, 0);
+
+ BitmapFontRegion lastGlyph = null;
+ for (var i = 0; i < text.Length;)
+ {
+ int character;
+ if (char.IsLowSurrogate(text[i]))
+ {
+ character = char.ConvertToUtf32(text[i - 1], text[i]);
+ i += 2;
+ }
+ else if (char.IsHighSurrogate(text[i]))
+ {
+ character = char.ConvertToUtf32(text[i], text[i - 1]);
+ i += 2;
+ }
+ else
+ {
+ character = text[i];
+ i += 1;
+ }
+
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (character)
+ {
+ case '\r':
+ continue;
+ case '\n':
+ offset.X = 0;
+ offset.Y += lineSpacing;
+ lastGlyph = null;
+ continue;
+ }
+
+ var fontRegion = bitmapFont.GetCharacterRegion(character);
+ if (fontRegion == null)
+ continue;
+
+ var transform1Matrix = transformMatrix;
+ transform1Matrix.M31 += offset.X + fontRegion.XOffset;
+ transform1Matrix.M32 += offset.Y + fontRegion.YOffset;
+
+ var textureRegion = fontRegion.TextureRegion;
+ var bounds = textureRegion.Bounds;
+ DrawSprite(textureRegion.Texture, ref transform1Matrix, ref bounds, color, flags, depth);
+
+ var advance = fontRegion.XAdvance + bitmapFont.LetterSpacing;
+ if (BitmapFont.UseKernings && lastGlyph != null)
+ {
+ int amount;
+ if (lastGlyph.Kernings.TryGetValue(character, out amount))
+ {
+ advance += amount;
+ }
+ }
+
+ offset.X += i != text.Length - 1
+ ? advance
+ : fontRegion.XOffset + fontRegion.Width;
+
+ lastGlyph = fontRegion;
+ }
+ }
+
+ ///
+ /// Draws unicode (UTF-16) characters as sprites using the specified , text
+ /// , position and optional , rotation
+ /// , origin , scale , and
+ /// depth .
+ ///
+ /// The .
+ /// The text .
+ /// The position .
+ ///
+ /// The . Use null to use the default
+ /// .
+ ///
+ ///
+ /// The angle (in radians) to rotate each sprite about its . The default
+ /// value is 0f.
+ ///
+ ///
+ /// The origin . Use null to use the default
+ /// .
+ ///
+ ///
+ /// The scale . Use null to use the default
+ /// .
+ ///
+ /// The . The default value is .
+ /// The depth . The default value is 0f
+ /// The method has not been called.
+ /// is null or is null.
+ public void DrawString(BitmapFont bitmapFont, StringBuilder text, Vector2 position, Color? color = null,
+ float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ Matrix2 transformMatrix;
+ Matrix2.CreateFrom(position, rotation, scale, origin, out transformMatrix);
+ DrawString(bitmapFont, text, ref transformMatrix, color, flags, depth);
+ }
+
+ ///
+ /// Draws unicode (UTF-16) characters as sprites using the specified , text
+ /// , transform and optional , origin
+ /// , , and depth .
+ ///
+ /// The .
+ /// The text .
+ /// The transform .
+ ///
+ /// The . Use null to use the default
+ /// .
+ ///
+ /// The . The default value is .
+ /// The depth . The default value is 0f
+ /// The method has not been called.
+ /// is null or is null.
+ public void DrawString(BitmapFont bitmapFont, string text, ref Matrix2 transformMatrix, Color? color = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ EnsureHasBegun();
+
+ if (bitmapFont == null)
+ throw new ArgumentNullException(nameof(bitmapFont));
+
+ if (text == null)
+ throw new ArgumentNullException(nameof(text));
+
+ var glyphs = bitmapFont.GetGlyphs(text);
+ foreach (var glyph in glyphs)
+ {
+ var transform1Matrix = transformMatrix;
+ transform1Matrix.M31 += glyph.Position.X;
+ transform1Matrix.M32 += glyph.Position.Y;
+
+ var texture = glyph.FontRegion.TextureRegion.Texture;
+ var bounds = texture.Bounds;
+ DrawSprite(texture, ref transform1Matrix, ref bounds, color, flags, depth);
+ }
+ }
+
+ ///
+ /// Draws unicode (UTF-16) characters as sprites using the specified , text
+ /// , position and optional , rotation
+ /// , origin , scale , and
+ /// depth .
+ ///
+ /// The .
+ /// The text .
+ /// The position .
+ ///
+ /// The . Use null to use the default
+ /// .
+ ///
+ ///
+ /// The angle (in radians) to rotate each sprite about its . The default
+ /// value is 0f.
+ ///
+ ///
+ /// The origin . Use null to use the default
+ /// .
+ ///
+ ///
+ /// The scale . Use null to use the default
+ /// .
+ ///
+ /// The . The default value is .
+ /// The depth . The default value is 0f
+ /// The method has not been called.
+ /// is null or is null.
+ public void DrawString(BitmapFont bitmapFont, string text, Vector2 position, Color? color = null,
+ float rotation = 0f, Vector2? origin = null, Vector2? scale = null,
+ FlipFlags flags = FlipFlags.None, float depth = 0f)
+ {
+ Matrix2 matrix;
+ Matrix2.CreateFrom(position, rotation, scale, origin, out matrix);
+ DrawString(bitmapFont, text, ref matrix, color, flags, depth);
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public struct DrawCallInfo : IBatchDrawCallInfo, IComparable
+ {
+ internal readonly PrimitiveType PrimitiveType;
+ internal int StartIndex;
+ internal int PrimitiveCount;
+ internal readonly Texture2D Texture;
+ internal readonly uint TextureKey;
+ internal readonly uint DepthKey;
+
+ internal unsafe DrawCallInfo(Texture2D texture, PrimitiveType primitiveType, int startIndex, int primitiveCount, float depth)
+ {
+ PrimitiveType = primitiveType;
+ StartIndex = startIndex;
+ PrimitiveCount = primitiveCount;
+ Texture = texture;
+ TextureKey = (uint)RuntimeHelpers.GetHashCode(texture);
+ DepthKey = *(uint*)&depth;
+ }
+
+ public void SetState(Effect effect)
+ {
+ var textureEffect = effect as ITextureEffect;
+ if (textureEffect != null)
+ textureEffect.Texture = Texture;
+ }
+
+ public bool TryMerge(ref DrawCallInfo drawCall)
+ {
+ if (PrimitiveType != drawCall.PrimitiveType || TextureKey != drawCall.TextureKey ||
+ DepthKey != drawCall.DepthKey)
+ return false;
+ drawCall.PrimitiveCount += PrimitiveCount;
+ return true;
+ }
+
+ [SuppressMessage("ReSharper", "ImpureMethodCallOnReadonlyValueField")]
+ public int CompareTo(DrawCallInfo other)
+ {
+ var result = TextureKey.CompareTo(other.TextureKey);;
+ if (result != 0)
+ return result;
+ result = DepthKey.CompareTo(other.DepthKey);
+ return result != 0 ? result : ((byte)PrimitiveType).CompareTo((byte)other.PrimitiveType);
+ }
+ }
+ }
+}
\ No newline at end of file
--
cgit v1.1-26-g67d0