1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
4 using System.Collections.Generic;
14 VertexPositionColorTexture VertexBL,
15 VertexPositionColorTexture VertexBR,
16 VertexPositionColorTexture VertexTL,
17 VertexPositionColorTexture VertexTR,
23 public static Vector2 GetMinPosition(params VertexPositionColorTexture[] vertices)
25 MathUtils.Min(vertices.Select(v => v.Position.X).ToArray()),
26 MathUtils.Min(vertices.Select(v => v.Position.Y).ToArray()));
28 public static Vector2 GetMaxPosition(params VertexPositionColorTexture[] vertices)
30 MathUtils.Max(vertices.Select(v => v.Position.X).ToArray()),
31 MathUtils.Max(vertices.Select(v => v.Position.Y).ToArray()));
33 public static Command FromTransform(
41 SpriteEffects effects,
45 int srcRectLeft = srcRect.Left;
46 int srcRectRight = srcRect.Right;
47 int srcRectTop = srcRect.Top;
48 int srcRectBottom = srcRect.Bottom;
49 if (effects.HasFlag(SpriteEffects.FlipHorizontally))
51 (srcRectRight, srcRectLeft) = (srcRectLeft, srcRectRight);
53 if (effects.HasFlag(SpriteEffects.FlipVertically))
55 (srcRectBottom, srcRectTop) = (srcRectTop, srcRectBottom);
58 float sin = (float)Math.Sin(rotationRad);
59 float cos = (float)Math.Cos(rotationRad);
61 var size = srcRect.Size.ToVector2() * scale;
63 Vector2 wAdd =
new Vector2(size.X * cos, size.X * sin);
64 Vector2 hAdd =
new Vector2(-size.Y * sin, size.Y * cos);
65 pos.X -= origin.X * scale.X * cos - origin.Y * scale.Y * sin;
66 pos.Y -= origin.Y * scale.Y * cos + origin.X * scale.X * sin;
68 var vertexTl =
new VertexPositionColorTexture
71 Position =
new Vector3(pos.X, pos.Y, 0f),
72 TextureCoordinate =
new Vector2((
float)srcRectLeft / (
float)texture.Width, (
float)srcRectTop / (
float)texture.Height)
75 var vertexTr =
new VertexPositionColorTexture
78 Position =
new Vector3(pos.X + wAdd.X, pos.Y + wAdd.Y, 0f),
79 TextureCoordinate =
new Vector2((
float)srcRectRight / (
float)texture.Width, (
float)srcRectTop / (
float)texture.Height)
82 var vertexBl =
new VertexPositionColorTexture
85 Position =
new Vector3(pos.X + hAdd.X, pos.Y + hAdd.Y, 0f),
86 TextureCoordinate =
new Vector2((
float)srcRectLeft / (
float)texture.Width, (
float)srcRectBottom / (
float)texture.Height)
89 var vertexBr =
new VertexPositionColorTexture
92 Position =
new Vector3(pos.X + wAdd.X + hAdd.X, pos.Y + wAdd.Y + hAdd.Y, 0f),
93 TextureCoordinate =
new Vector2((
float)srcRectRight / (
float)texture.Width, (
float)srcRectBottom / (
float)texture.Height)
96 var min = GetMinPosition(
102 var max = GetMaxPosition(
120 public bool Overlaps(
Command other)
123 Min.X <= other.Max.X &&
Max.X >= other.Min.X &&
124 Min.Y <= other.Max.Y &&
Max.Y >= other.Min.Y;
128 private struct RecordedBuffer
130 public readonly Texture2D Texture;
131 public readonly VertexBuffer VertexBuffer;
132 public readonly
int PolyCount;
134 public RecordedBuffer(List<Command> commandList,
int startIndex,
int count)
136 Texture = commandList[startIndex].Texture;
138 VertexBuffer =
new VertexBuffer(
GameMain.
Instance.GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, count * 4, BufferUsage.WriteOnly);
139 VertexPositionColorTexture[] vertices =
new VertexPositionColorTexture[count * 4];
140 for (
int i = 0; i < count; i++)
142 vertices[(i * 4) + 0] = commandList[startIndex + i].VertexBL;
143 vertices[(i * 4) + 1] = commandList[startIndex + i].VertexBR;
144 vertices[(i * 4) + 2] = commandList[startIndex + i].VertexTL;
145 vertices[(i * 4) + 3] = commandList[startIndex + i].VertexTR;
147 VertexBuffer.SetData(vertices);
149 PolyCount = count * 2;
155 private readonly List<RecordedBuffer> recordedBuffers =
new List<RecordedBuffer>();
156 private readonly List<Command> commandList =
new List<Command>();
157 private SpriteSortMode currentSortMode;
159 private IndexBuffer indexBuffer =
null;
160 private int maxSpriteCount = 0;
163 private volatile bool isDisposed =
false;
165 public Vector2
Min {
get;
private set; }
166 public Vector2
Max {
get;
private set; }
168 public void Begin(SpriteSortMode sortMode)
171 currentSortMode = sortMode;
174 private void AppendCommand(
Command command)
176 if (isDisposed) {
return; }
178 if (commandList.Count == 0) {
Min = command.Min;
Max = command.Max; }
179 Min =
new Vector2(Math.Min(command.Min.X,
Min.X), Math.Min(command.Min.Y,
Min.Y));
180 Max =
new Vector2(Math.Max(command.Max.X,
Max.X), Math.Max(command.Max.Y,
Max.Y));
182 commandList.Add(command);
185 public void Draw(Texture2D texture, Vector2 pos, Rectangle? srcRect, Color color,
float rotationRad, Vector2 origin, Vector2 scale, SpriteEffects effects,
float depth)
187 if (isDisposed) {
return; }
189 var command =
Command.FromTransform(texture, pos, srcRect ?? texture.Bounds, color, rotationRad, origin, scale, effects, depth, commandList.Count);
190 AppendCommand(command);
193 public void Draw(Texture2D texture, VertexPositionColorTexture[] vertices,
float layerDepth,
int? count =
null)
195 if (isDisposed) {
return; }
197 int iters = count ?? (vertices.Length / 4);
198 for (
int i=0;i<iters;i++)
200 var subset = vertices[((i * 4) + 0)..((i * 4) + 4)];
208 Command.GetMinPosition(subset),
209 Command.GetMaxPosition(subset),
211 AppendCommand(command);
217 if (isDisposed) {
return; }
220 switch (currentSortMode)
222 case SpriteSortMode.FrontToBack:
223 commandList.Sort((c1, c2) =>
225 return c1.Depth < c2.Depth ? -1
226 : c1.Depth > c2.Depth ? 1
227 : c1.Index < c2.Index ? 1
228 : c1.Index > c2.Index ? -1
232 case SpriteSortMode.BackToFront:
233 commandList.Sort((c1, c2) =>
235 return c1.Depth < c2.Depth ? 1
236 : c1.Depth > c2.Depth ? -1
237 : c1.Index < c2.Index ? 1
238 : c1.Index > c2.Index ? -1
247 for (
int i = 1; i < commandList.Count; i++)
249 if (commandList[i].Texture != commandList[i - 1].Texture)
251 for (
int j = i - 1; j >= 0; j--)
253 if (commandList[j].Texture == commandList[i].Texture)
258 commandList.SiftElement(i, j + 1);
261 else if (commandList[j].Overlaps(commandList[i]))
272 if (isDisposed) {
return; }
275 CrossThread.RequestExecutionOnMainThread(() =>
277 if (isDisposed) {
return; }
278 if (commandList.Count == 0) { return; }
280 for (
int i = 1; i < commandList.Count; i++)
282 if (commandList[i].Texture != commandList[startIndex].Texture)
284 maxSpriteCount = Math.Max(maxSpriteCount, i - startIndex);
285 recordedBuffers.Add(
new RecordedBuffer(commandList, startIndex, i - startIndex));
289 recordedBuffers.Add(
new RecordedBuffer(commandList, startIndex, commandList.Count - startIndex));
290 maxSpriteCount = Math.Max(maxSpriteCount, commandList.Count - startIndex);
311 int requiredIndexCount = maxSpriteCount * 6;
312 if (requiredIndexCount > 0 && (indexBuffer ==
null || indexBuffer.IndexCount < requiredIndexCount))
314 indexBuffer?.Dispose();
315 indexBuffer =
new IndexBuffer(gfxDevice, IndexElementSize.SixteenBits, requiredIndexCount * 2, BufferUsage.WriteOnly);
316 ushort[] indices =
new ushort[requiredIndexCount * 2];
317 for (
int i = 0; i < indices.Length; i += 6)
319 indices[i + 0] = (ushort)((i / 6) * 4 + 1);
320 indices[i + 1] = (ushort)((i / 6) * 4 + 0);
321 indices[i + 2] = (ushort)((i / 6) * 4 + 2);
322 indices[i + 3] = (ushort)((i / 6) * 4 + 1);
323 indices[i + 4] = (ushort)((i / 6) * 4 + 2);
324 indices[i + 5] = (ushort)((i / 6) * 4 + 3);
326 indexBuffer.SetData(indices);
329 gfxDevice.Indices = indexBuffer;
330 for (
int i = 0; i < recordedBuffers.Count; i++)
332 gfxDevice.SetVertexBuffer(recordedBuffers[i].VertexBuffer);
335 gfxDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, recordedBuffers[i].PolyCount);
342 foreach (var buffer
in recordedBuffers)
344 buffer.VertexBuffer.Dispose();
346 recordedBuffers.Clear();
348 indexBuffer?.Dispose(); indexBuffer =
null;
void Draw(Texture2D texture, VertexPositionColorTexture[] vertices, float layerDepth, int? count=null)
static BasicEffect BasicEffect
void Draw(Texture2D texture, Vector2 pos, Rectangle? srcRect, Color color, float rotationRad, Vector2 origin, Vector2 scale, SpriteEffects effects, float depth)
readonly record struct Command(Texture2D Texture, VertexPositionColorTexture VertexBL, VertexPositionColorTexture VertexBR, VertexPositionColorTexture VertexTL, VertexPositionColorTexture VertexTR, float Depth, Vector2 Min, Vector2 Max, int Index)
volatile bool ReadyToRender
void Begin(SpriteSortMode sortMode)