434 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
} |