434 lines
12 KiB
C#

using Dashboard.Media;
using OpenTK.Mathematics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Dashboard.ImmediateDraw
{
public class DrawList : IEnumerable<Frame>
{
private readonly List<Frame> _frames = new List<Frame>();
public void Clear()
{
_frames.Clear();
}
protected void Enqueue(in Frame frame)
{
_frames.Add(frame);
}
public void Invoke(CommandHandler handler)
{
Enqueue(Command.Invoke);
Enqueue(new Frame(handler));
}
public void ConditionalBegin(bool value)
{
Enqueue(Command.ConditionalBegin);
Enqueue((Frame)(value ? 1 : 0));
}
public void ConditionalBegin(Func<bool> condition)
{
Enqueue(Command.ConditionalBegin);
Enqueue(new Frame(condition));
}
public void ConditionalEnd()
{
Enqueue(Command.ConditionalEnd);
}
public void PushViewport()
{
Enqueue(Command.PushViewport);
}
public void IntersectViewport(in Rectangle viewport)
{
Enqueue(Command.IntersectViewport);
Enqueue(viewport);
}
public void StoreViewport(in Rectangle viewport)
{
Enqueue(Command.StoreViewport);
Enqueue(viewport);
}
public void PopViewport()
{
Enqueue(Command.PopViewport);
}
public void PushZ()
{
Enqueue(Command.PushZ);
}
public void IncrementZ()
{
Enqueue(Command.IncrementZ);
}
public void AddZ(int value)
{
if (value == 1)
{
IncrementZ();
}
else if (value == -1)
{
DecrementZ();
}
else
{
Enqueue(Command.AddZ);
Enqueue((Frame)value);
}
}
public void StoreZ(int value)
{
Enqueue(Command.StoreZ);
Enqueue((Frame)value);
}
public void DecrementZ()
{
Enqueue(Command.DecrementZ);
}
public void PopZ()
{
Enqueue(Command.PopZ);
}
public void PushStyle(Style style)
{
Enqueue(Command.PushStyle);
Enqueue(new Frame(style));
}
public void StoreStyle(Style style)
{
Enqueue(Command.StoreStyle);
Enqueue(new Frame(style));
}
public void PopStyle()
{
Enqueue(Command.PopStyle);
}
public void Line(in Line line)
{
Enqueue(Command.Line);
Enqueue(line);
}
public void Line(params Line[] lines)
{
Enqueue(Command.Line);
Enqueue((Frame)lines.Length);
foreach (Line line in lines)
Enqueue(line);
}
public void Bezier(in Bezier bezier)
{
Frame a, b;
Frame.Create(bezier, out a, out b);
Enqueue(Command.Bezier);
Enqueue(a);
Enqueue(b);
}
public void Bezier(params Bezier[] beziers)
{
Frame a, b;
Enqueue(Command.Bezier);
Enqueue((Frame)beziers.Length);
foreach (Bezier bezier in beziers)
{
Frame.Create(bezier, out a, out b);
Enqueue(a);
Enqueue(b);
}
}
public void Rectangle(in Rectangle rectangle)
{
Enqueue(Command.Rectangle);
Enqueue(rectangle);
}
public void Rectangle(Rectangle[] rectangles)
{
Enqueue(Command.Rectangle);
Enqueue((Frame)rectangles.Length);
foreach (Rectangle rectangle in rectangles)
Enqueue(rectangle);
}
public void Ellipse(in Ellipse ellipse)
{
Frame a, b;
Frame.Create(ellipse, out a, out b);
Enqueue(Command.Ellipse);
Enqueue(a);
Enqueue(b);
}
public void Ellipse(params Ellipse[] ellipses)
{
Frame a, b;
Enqueue(Command.Ellipse);
Enqueue((Frame)ellipses.Length);
foreach (Ellipse ellipse in ellipses)
{
Frame.Create(ellipse, out a, out b);
Enqueue(a);
Enqueue(b);
}
}
public void Triangle(in Triangle triangle)
{
Enqueue(Command.Triangle);
Enqueue(triangle.A);
Enqueue(triangle.B);
Enqueue(triangle.C);
}
public void Triangle(params Triangle[] triangles)
{
Enqueue(Command.Triangle);
Enqueue((Frame)triangles.Length);
foreach (Triangle triangle in triangles)
{
Enqueue(triangle.A);
Enqueue(triangle.B);
Enqueue(triangle.C);
}
}
public void Polygon(params Vector2[] polygon)
{
Enqueue(Command.Polygon);
Enqueue((Frame)polygon.Length);
foreach (Vector2 vertex in polygon)
{
Enqueue(vertex);
}
}
public void Image(Image texture, in Rectangle rectangle)
{
Enqueue(Command.Image);
Enqueue((Frame)(int)ImageCommandFlags.Single);
Enqueue(new Frame(texture));
Enqueue(rectangle);
}
public void Image(Image texture, in Rectangle rectangle, in Rectangle uv)
{
Enqueue(Command.Image);
Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
Enqueue(new Frame(texture));
Enqueue(rectangle);
Enqueue(uv);
}
public void Image(Image texture, ReadOnlySpan<Rectangle> rectangles, bool interleavedUV = false)
{
int count = rectangles.Length;
ImageCommandFlags flags = ImageCommandFlags.None;
if (interleavedUV)
{
count /= 2;
flags |= ImageCommandFlags.UVs;
}
Enqueue(Command.Image);
Enqueue(new Frame((int)flags, count));
Enqueue(new Frame(texture));
foreach (Rectangle rectangle in rectangles)
{
Enqueue(rectangle);
}
}
public void Image(Image texture, ReadOnlySpan<Rectangle> rectangles, ReadOnlySpan<Rectangle> uvs)
{
int count = Math.Min(rectangles.Length, uvs.Length);
Enqueue(Command.Image);
Enqueue(new Frame((int)ImageCommandFlags.UVs, count));
Enqueue(new Frame(texture));
for (int i = 0; i < count; i++)
{
Enqueue(rectangles[i]);
Enqueue(uvs[i]);
}
}
public void Image3D(Image texture, in Image3DCall call)
{
Enqueue(Command.Image);
Enqueue(new Frame(ImageCommandFlags.Image3d | ImageCommandFlags.Single));
Enqueue(new Frame(texture));
Enqueue(call.Rectangle);
Enqueue(call.UVs);
Enqueue(new Frame(call.Layer));
}
public void Image3D(Image texture, ReadOnlySpan<Image3DCall> calls)
{
Enqueue(Command.Image);
Enqueue(new Frame((int)ImageCommandFlags.Image3d, calls.Length));
Enqueue(new Frame(texture));
foreach (Image3DCall call in calls)
{
Enqueue(call.Rectangle);
Enqueue(call.UVs);
Enqueue(new Frame(call.Layer));
}
}
public void Splice(DrawList list)
{
foreach (Frame frame in list)
{
Enqueue(frame);
}
}
/// <summary>
/// Serialize an object into the command list.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">What to write into the command list.</param>
public void Write<T>(T value)
{
DrawingEngine.GetSerializer(value).Serialize(value, this);
}
/// <summary>
/// Serialize an object into the command list.
/// </summary>
/// <typeparam name="T">The type of the value to serialize.</typeparam>
/// <param name="value">What to write into the command list.</param>
public void Write<T>(IDrawListSerializable<T> value)
where T : IDrawListSerializable<T>, new()
{
DrawingEngine.GetSerializer(value).Serialize((T)value, this);
}
public DrawQueue GetEnumerator() => new DrawQueue(_frames);
IEnumerator<Frame> IEnumerable<Frame>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class DrawQueue : IEnumerator<Frame>
{
private readonly IReadOnlyList<Frame> _frames;
private int _current;
public Frame Current => _frames[_current];
object IEnumerator.Current => Current;
public DrawQueue(IReadOnlyList<Frame> frames)
{
_current = -1;
_frames = frames;
}
public void Dispose()
{
}
public bool TryDequeue([NotNullWhen(true)] out Frame frame)
{
if (MoveNext())
{
frame = Current;
return true;
}
else
{
frame = default;
return false;
}
}
public Frame Dequeue() => TryDequeue(out Frame frame) ? frame : throw new Exception("No more frames left.");
public bool TryPeek([NotNullWhen(true)] out Frame frame)
{
if (_current + 1 < _frames.Count)
{
frame = _frames[_current + 1];
return true;
}
else
{
frame = default;
return false;
}
}
public Frame Peek() => TryPeek(out Frame frame) ? frame : throw new Exception("No more frames left.");
/// <summary>
/// Deserialize an object from the command queue.
/// </summary>
/// <typeparam name="T">Type of the object to deserialize.</typeparam>
/// <param name="value">The deserialized value.</param>
public void Read<T>([NotNull] out T? value)
{
value = DrawingEngine.GetSerializer(default(T)).Deserialize(this);
}
/// <summary>
/// Deserialize an object from the command queue.
/// </summary>
/// <typeparam name="T">Type of the object to deserialize.</typeparam>
/// <param name="value">The deserialized value.</param>
public void Read<T>([NotNull] out IDrawListSerializable<T>? value)
where T : IDrawListSerializable<T>, new()
{
value = DrawingEngine.GetSerializer(value = null).Deserialize(this);
}
/// <inheritdoc/>
public bool MoveNext()
{
if (_current + 1 < _frames.Count)
{
_current++;
return true;
}
return false;
}
/// <inheritdoc/>
public void Reset()
{
_current = -1;
}
}
}